React、Redux:如何避免重复将数据加载到Store中
嘿,这个场景我太熟悉了!之前做项目的时候也遇到过多个独立组件需要同一份Redux数据的情况,核心解决思路就是把数据加载的判断逻辑从组件里移到Redux的业务逻辑层,或者统一到路由/全局初始化阶段,这样就能避免重复请求和重复存储了。给你几个具体的实现方案:
方案1:在Thunk/Action Creator里加状态判断(最推荐)
其实你现在组件里的if(!this.props.loaded)判断可以直接移到loadRooms(应该是loadCats吧?笔误?)这个thunk里,让Redux自己来决定要不要发起请求。这样不管多少个组件调用这个action,只要Store里已经标记loaded: true,就不会重复发API请求。
比如修改你的thunk:
export const loadCats = () => (dispatch, getState) => { // 先从Store里拿当前的状态 const { cats } = getState(); // 如果已经加载完成,直接返回,不执行后续请求逻辑 if (cats.loaded) { return Promise.resolve(); } // 否则正常发起请求 dispatch({ type: 'CATS_LOAD_START' }); return fetch('/api/cats') .then(res => res.json()) .then(data => { dispatch({ type: 'CATS_LOAD_SUCCESS', payload: { items: data, loaded: true } }); }) .catch(err => { dispatch({ type: 'CATS_LOAD_FAILURE', payload: err }); }); };
这样你的组件里的componentDidMount可以不用改——哪怕两个组件都调用this.props.actions.loadCats(),thunk内部会先检查Store状态,只会发起一次请求,数据也只会存一次到Store里。
方案2:在路由/全局层面提前加载数据
如果这两个组件都属于同一个路由页面,或者你的应用启动时就大概率需要这份cats数据,那可以把数据加载逻辑放到路由的父组件里,或者应用的根组件App.js的生命周期里。
比如用路由容器组件:
class CatsPageContainer extends React.Component { componentDidMount() { // 进入这个路由时就加载数据,不管子组件是谁 this.props.loadCats(); } render() { // 渲染路由对应的子组件(包括你那两个独立组件) return this.props.children; } } // 连接Redux export default connect( state => ({ loaded: state.cats.loaded }), { loadCats } )(CatsPageContainer);
然后在路由配置里把这个容器作为路由的组件:
<Route path="/cats" component={CatsPageContainer}> <Route path="/cats/list" component={CatListComponent} /> <Route path="/cats/filter" component={CatFilterComponent} /> </Route>
这样只要进入/cats下的任何子路由,数据都会提前加载好,两个子组件直接用Store里的数据就行,不用各自判断加载。
方案3:用自定义Hook封装加载逻辑(函数组件场景)
如果你的组件已经换成了函数组件,可以写一个自定义Hook来封装数据加载的逻辑,让多个组件共享:
import { useSelector, useDispatch } from 'react-redux'; import { useEffect } from 'react'; import { loadCats } from './actions/catsActions'; export const useCatsData = () => { const { cats, loaded } = useSelector(state => state.cats); const dispatch = useDispatch(); useEffect(() => { if (!loaded) { dispatch(loadCats()); } }, [loaded, dispatch]); return { cats, loaded }; };
然后你的两个组件只要调用这个Hook就行:
const CatListComponent = () => { const { cats, loaded } = useCatsData(); // 渲染逻辑... }; const CatFilterComponent = () => { const { cats, loaded } = useCatsData(); // 渲染逻辑... };
同样,因为thunk里已经有状态判断,所以只会发起一次请求。
总的来说,最灵活的是方案1,因为它不依赖路由结构,不管组件在哪里,只要调用action就会自动判断是否需要加载数据,完全解耦了组件和数据加载的逻辑。
内容的提问来源于stack exchange,提问作者crowhill

