起因
- 偶然间看了一下
Angular
的服务的依赖注入和rxjs
的观察者流的使用,觉得还有点意思,就想在React
中实现一下类似的。
准备工作
- 一个空的
React
项目。
- 安装
rxjs
,npm install rxjs --save
;
实现思路
- 首先是根据业务需求,将需要抽离的全局状态使用不同的服务类进行包裹,进行数据分离。
- 实现一个自定的
Provider
和connect
方法,将对应的全局状态和服务进行组件的注入。
- 在组件中编写
mapStateToProps
自定义需要获取的服务以及全局状态。
代码实现
// 用来包裹服务类,生成一个实例存到闭包中,保证每次拿到的服务对象是同一个实例
const wrapService = (Service, initData = {}) => {
const service = new Service(initData);
return () => {
return service;
}
}
// 一个服务类
import { Subject } from 'rxjs';
import { wrapService } from 'Utils/service';
class TestService {
constructor(initData = {}) {
this.data = initData;
}
data$ = new Subject();
subscribe = (callback) => {
this.subscription = this.data$.subscribe(callback);
this.data$.next(this.data);
return () => {
this.subscription.unsubscribe();
}
}
add = (data) => {
this.data.count = Number(data.count) + Number(this.data.count);
this.data$.next(this.data);
}
set = (data) => {
this.data = { ...this.data, ...data };
this.data$.next(this.data);
}
}
const initData = {
count: 0,
test1: 'Hello',
test2: 'World'
};
export default wrapService(TestService, initData)();
// 获取唯一的context
const getContext = (() => {
const Context = React.createContext(null);
Context.displayName = 'serviceProvider';
return () => {
return Context;
}
})();
// 默认注入所有的全局状态
const defaultMapStateToProps = (data) => {
return {
...data
};
}
/**
* 注入服务的高阶组件
*/
function ServiceProvider({ services = {}, children = null }) {
const Context = getContext();
const [contextValue, setContextValue] = useState({
...services
});
const unsubscribeList = [];
useEffect(() => {
Object.keys(services).forEach((key) => {
const unsubscribe = services[key].subscribe(_data => {
setContextValue({
...contextValue,
..._data
});
});
unsubscribeList.push(unsubscribe);
});
return () => {
unsubscribeList.forEach(unsubscribe => {
typeof unsubscribe === 'function' && unsubscribe();
});
}
}, [])
return (
<Context.Provider value={contextValue}>{children}</Context.Provider>
)
}
// 向组件中注入全局的状态和服务对象
const inject = (mapStateToProps = defaultMapStateToProps) => {
const Context = getContext();
return (Component) => (props) => {
return (
<Context.Consumer>
{
data => {
const newProps = mapStateToProps(data);
return <Component {...props} {...newProps} />
}
}
</Context.Consumer>
)
}
}
// 使用,先在最外层的render中使用Provider包裹。
ReactDOM.render(
<ServiceProvider services={{ testService: testService }}>
<Index />
</ServiceProvider>
,
document.getElementById("root")
// 然后在需要的地方,使用inject方法对组件进行包裹。
const mapStateToProps = (data) => {
const {
count,
testService
} = data;
return {
count,
testService
}
}
export default inject(mapStateToProps)(Subscribe);
// 这样就在Subscribe组件中引入了对应的全局状态count和服务testService,直接在props中可以拿到。
小结