最近写业务项目,已有代码是mobx+react class,正好想用react hook,于是做了如下修改下实现支持
代码职责
- store 负责业务数据定义存储
- action 负责操作store中数据
- component 通过action完成数据操作,使用store数据进行内容展示,state负责ui交互状态
已有代码
Provider
import { Provider } from 'mobx-react';
export class ContainerProvider extends React.Component<any, any> {
private injects: {
[injectName: string]: any
} = {};
UNSAFE_componentWillMount() {
const instances = injectInstance(
assign({}, this.props.inject)
);
// 复杂app可以此做些初始化工作
this.injects = {};
instances.forEach((value, key) => {
this.injects[key] = value;
});
}
render() {
return (
<Provider {...this.injects}>
{this.props.children}
</Provider>
);
}
}
inject
export const injectDecorator = (injectName: string): any => (target: any, propertyKey: string, descriptor: PropertyDescriptor): any => {
target[propertyKey] = injectName;
// 加入一个标注变量
if (!target['_injectVariables_']) {
target['_injectVariables_'] = [propertyKey];
} else {
target['_injectVariables_'].push(propertyKey);
}
return descriptor;
};
export const injectInstance = (classes: {}) => {
const classMap = new Map<string, any>();
const instanceMap = new Map<string, any>();
// 实例化
Object.keys(classes).forEach((eachClassName) => {
if (classMap.has(eachClassName)) {
throw new Error(`duplicate className: ${eachClassName}`);
}
classMap.set(eachClassName, classes[eachClassName]);
});
classMap.forEach((eachClass: any, keyName) => {
instanceMap.set(keyName, new eachClass());
});
// 注入
instanceMap.forEach((eachInstance: any, key: string) => {
// 遍历对象中注入类名
eachInstance['_injectVariables_'] && eachInstance['_injectVariables_'].forEach((injectVariableKey: string) => {
if (!instanceMap.get(eachInstance[injectVariableKey])) {
throw new Error('injectName: ' + eachInstance[injectVariableKey] + ' not found!');
}
// 注入名改成实际对象
eachInstance[injectVariableKey] = instanceMap.get(eachInstance[injectVariableKey]);
});
delete eachInstance['_injectVariables_'];
});
return instanceMap;
};
Store
export class DemoStore {
@observable data={}
}
Action
export class DemoAction {
@inject('DemoStore') store: DemoStore;
async getData() {
this.store.data = await fetch(...);
}
}
Component
interface Props {
DemoStore?: DemoStore
DemoAction?: DemoAction
}
@inject('DemoStore', 'DemoAction')
@observer
export class Demo extends Component<Props, any> {
render() {
const { data } = this.props.DemoStore;
return
<div onClick=()=>{this.props.DemoAction.getData()}>
{data}
</div>
}
}
新增代码
Provider
export const StoreContext = createContext(null);
export const ContextProvider = observer((props: any) => {
const instances = injectInstance(
assign({}, props.inject)
);
const injects = {};
instances.forEach((value, key) => {
injects[key] = value;
});
return (
<StoreContext.Provider value={injects}>
{props.children}
</StoreContext.Provider>
);
});
useInject
export const useInject = (name) => {
const context = useContext(StoreContext);
if (name && context) {
return context[name];
}
return context;
};
Store
// 保持不变
Action
// 保持不变
@inject('DemoStore') store: DemoStore;
// 注意可能获取不到的情况,可放到init方法中专门调用进行初始化
// store: UCenterStore = useInject('DemoStore');
Component
import { useObserver } from 'mobx-react-lite';
export const Demo = memo((props: PropsDefine) => {
const action: DemoAction = useInject('DemoAction');
const store: DemoStore = useInject('DemoStore');
useEffect(() => {
action.getData();
}, []);
const handleClick = useCallback((item, index) => {
// action.listItemClick(item, index);
// action.log({
// k: 'h5_click',
// v: 'hot_words'
// });
}, []);
const { data } = store;
return useObserver(() => (
<div className='creater-page'>
...
</div>
))
}
总结
新Provider仍然通过inject实例化及赋值对象,action&store写码方式保持不变,component中使用hook,通过usrInject获得action及store实例,通过useObserver实现ui对store的响应