解决问题:
1.多个页面都要使用的公用数据,会在项目初始化进来时就全部获取,并缓存store。当公用数据越很多时,项目打开时就会很卡。
需要: 实时按需加载数据,并且整个项目只发起一次http请求获取数据,获取后存store。当其他页面也用到这些数据时,不要再重复发起http请求。
2.当数据刷新时,各页面使用该数据的组件都要实时刷新。因此每个使用这些数据的页面组件都要用@observer。观察后会发现,这些使用同样数据页面,实现的功能也类似,比如都是做商店列表下拉框。代码重复量很多。
需要: 将不同页面使用这些数据的相同功能,整合在一个组件。这样不同的页面就可以复用组件的state,不用去observe很多的页面组件。
项目使用时间比较久。react的class组件,和升级后的function函数组件都有。管理本地缓存数据,用的是 mobx-react 的provider&inject的方式。下面是大概的用法示例。
mobx-react
// 首先父组件
import { inject, Provider } from 'mobx-react';
import stores from 'SRC/store';
@observer
class App extends Component {
render() {
return (
<Provider {...stores}>
<Routes />
</Provider>
)
}
}
// 然后子组件(很多子页面需要使用shopList)
// 函数式组件不接受@修饰符的语法,参考官网使用inject('store')(observer(Component)) 这种语法方式。
import { inject, observer } from 'mobx-react';
function TestChild(prop) {
const { shopStore } = prop;
...
}
export default inject('shopStore')(observer(TestChild))
自定义hook, 复用useState,实时刷新公用组件
在线demo codesandbox.io/s/mobx-and-…
思路步骤:
- 数据shopList在不同页面都使用了,因为不希望每次用到shopList,都发起http重新获取。先定义mobx store缓存获取的shopList。
- 因为不想在项目进来时就发起http,需要按需加载数据。定义一个公用的hook,在hook中判断store是否已经缓存了数据或者已经在请求。都没有情况下,发起http请求。
- hook拿到store中的shopList。 并且observe shopList这个值,根据这个值的变化,实时刷新state。
- 公用组件ShopSelect,把不同页面的商店列表选择框的功能整合。这个公用的组件需要的数据shopList是一样的,并且页面要根据数据实时刷新。所以直接引用hook中的state就可以。const [shopList] = useShop();
- 各页面使用公用组件ShopSelect
mobx store data
class Shop {
loading = false;
shopList = [];
constructor() {
makeObservable(this, {
loading: observable,
shopList: observable,
getShopList: computed,
getDataSource: action
});
}
get getShopList() {
return toJS(this.shopList);
}
async getDataSource() {
this.loading = true;
try {
const data = await httpApi.selectShopList();
if (data.length > 0) this.loading = false;
this.setshopList(data);
} catch (error) {
return Promise.reject(error);
}
}
setshopList(data) {
this.shopList = data;
}
}
export default new Shop();
自定义hook, 监听mobx 存储的data,数据变化时set state, 刷新公用组件
import { useState } from "react";
import shopStore from "./store/shop";
import { observe } from "mobx";
// 自定义hook复用state
export default function useShop() {
const [shopList, setShopList] = useState(shopStore.getShopList);
// 只http 请求一次
if (shopStore.getShopList.length === 0 && !shopStore.loading) {
shopStore.getDataSource();
}
// 监听getShopList值,实时修改state
observe(shopStore, "getShopList", ({ newValue, oldValue }) => {
if (newValue !== oldValue) {
setShopList(shopStore.getShopList);
}
});
return [shopList];
}
公用组件ShopSelect,使用自定义hook useShop
import { Select } from "antd";
import useShop from "../useShop";
const ShopSelect = (props) => {
const { children, showShopList, ...other } = props;
// 此处使用自定义hook
const [shopList] = useShop();
return (
<Select {...other} virtual={false}>
{children}
{showShopList(shopList)}
</Select>
);
};
ShopSelect.defaultProps = {
children: null,
//showShopList,自定义Select.Option的方式,默认如下
showShopList: (list) =>
list.map((item) => (
<Select.Option key={item.id} value={item.id}>
{item.name}
</Select.Option>
))
};
export { ShopSelect };
各页面使用公用组件ShopSelect
import { Select } from "antd";
import { ShopSelect } from "./component/shopSelect";
import "./styles.css";
export default function App() {
return (
<div className="App">
<ShopSelect style={{ width: 200 }} placeholder="请选择商铺">
<Select.Option value={null}>全部</Select.Option>
</ShopSelect>
</div>
);
}