如何在多个通用组件间拆分Redux Store并隔离状态?
这个问题其实是Redux中多实例组件共享单一状态时的典型冲突——所有VendorResults组件都订阅同一个reducer的状态,最后完成的异步请求自然会覆盖掉之前的状态。好在我们不用新增组件或拆分action/reducer,只需要调整state的结构和action的传递逻辑,就能实现状态隔离,同时保持组件的通用性。
核心思路
把Redux Store中对应厂商的状态,从单一对象改成以厂商名称为Key的对象集合,让每个VendorResults组件只订阅自己对应Key的状态;同时在action中携带厂商标识,让reducer知道要更新哪个厂商的状态块。
1. 重构Reducer的状态结构
原来的reducer维护的是单一全局状态,现在我们改成按厂商拆分的结构,每个厂商拥有独立的fetching、payload、error状态:
// 定义单个厂商的初始状态模板 const vendorInitialState = { payload: [], fetching: false, error: null }; // 全局初始状态改为空对象,后续动态添加厂商状态 const initialState = {}; export default function(state = initialState, action){ const { vendor } = action; // 从action中拿到当前操作的厂商标识 if (!vendor) return state; // 没有厂商标识时直接返回原状态 switch (action.type){ case FETCHING_VENDOR: return { ...state, [vendor]: { // 动态更新对应厂商的状态 ...vendorInitialState, fetching: true } }; case FETCH_VENDOR_SUCCESS: return { ...state, [vendor]: { ...vendorInitialState, payload: action.payload.data, fetching: false } }; case VENDOR_ERROR: return { ...state, [vendor]: { ...vendorInitialState, error: action.payload, fetching: false } }; default: return state; } }
2. 修改Action,携带厂商标识
我们需要在每个action中传入厂商名称,让reducer明确知道要处理哪个厂商的状态更新:
// 先修改action creator,添加vendor参数 function getVendor(vendor) { return { type: FETCHING_VENDOR, vendor }; } function getVendorSuccess(data, vendor) { return { type: FETCH_VENDOR_SUCCESS, payload: data, vendor }; } function getVendorError(error, vendor) { return { type: VENDOR_ERROR, payload: error, vendor }; } // 再修改Thunk action,传递vendor到每个dispatch的action中 export function getVendorInformation(vendor) { const url = `${VENDOR_URL}api/search/${vendor}`; return dispatch => { dispatch(getVendor(vendor)); fetch(url) .then(blob => blob.json()) .then(data => { dispatch(getVendorSuccess(data, vendor)) }) .catch(e => { console.log(e); dispatch(getVendorError(e.message, vendor)) }); };
3. 组件中映射对应厂商的状态
最后,让每个VendorResults组件只订阅自己对应厂商的状态,而不是全局状态:
// 在组件的mapStateToProps中,通过ownProps拿到传入的vendor,匹配对应状态 const mapStateToProps = (state, ownProps) => { // 如果该厂商还没有状态,返回初始模板避免报错 const vendorState = state.vendorResults[ownProps.vendor] || vendorInitialState; return { ...vendorState }; }; // 组件内部的逻辑不需要改动,依然通过props获取状态 componentDidMount() { const {vendor} = this.props; this.props.getVendorInformation(vendor); }
可选优化:处理组件卸载时的请求
如果组件在请求完成前被卸载,可能会触发无效的状态更新,我们可以用AbortController中断请求:
// 组件中添加请求中断逻辑 componentDidMount() { const { vendor } = this.props; const controller = new AbortController(); this.abortController = controller; this.props.getVendorInformation(vendor, controller.signal); } componentWillUnmount() { this.abortController?.abort(); } // 修改Thunk action接收signal参数 export function getVendorInformation(vendor, signal) { const url = `${VENDOR_URL}api/search/${vendor}`; return dispatch => { dispatch(getVendor(vendor)); fetch(url, { signal }) .then(blob => blob.json()) .then(data => { dispatch(getVendorSuccess(data, vendor)) }) .catch(e => { // 忽略请求中止的错误 if (e.name !== 'AbortError') { console.log(e); dispatch(getVendorError(e.message, vendor)) } }); };
这样调整后,所有VendorResults组件的状态完全隔离,同时依然复用同一个组件、action和reducer,完美满足你的需求。
内容的提问来源于stack exchange,提问作者jmedran
相关产品推荐
相关产品推荐

