React/Redux中监听变量变化并更新状态的实现方案咨询
嘿,针对你在React/Redux里监听变量变化、更新状态的需求,结合你用三个sub字段求和的场景,我给你梳理几个靠谱的实现方案,你可以根据自己的场景选~
方案1:组件内部监听props变化(最直接适配你的场景)
如果你的求和逻辑只在某个组件里用到,直接在组件内部监听sub1、sub2、sub3的变化,然后计算总和并更新Redux状态是最直接的方式。
类组件实现(用componentDidUpdate)
class MyComponent extends React.Component { componentDidUpdate(prevProps) { // 对比前一次props和当前props,只有当三个sub字段有变化时才执行计算 if ( prevProps.sub1 !== this.props.sub1 || prevProps.sub2 !== this.props.sub2 || prevProps.sub3 !== this.props.sub3 ) { const total = this.props.sub1 + this.props.sub2 + this.props.sub3; // 触发action更新Redux中的total状态 this.props.updateTotal(total); } } render() { return ( <div> <p>当前总和:{this.props.total}</p> </div> ); } } // 映射Redux状态和action到组件props const mapStateToProps = state => ({ sub1: state.form.sub1, sub2: state.form.sub2, sub3: state.form.sub3, total: state.form.total }); const mapDispatchToProps = dispatch => ({ updateTotal: total => dispatch({ type: 'UPDATE_TOTAL', payload: total }) }); export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
注意:一定要加props对比的条件判断,不然componentDidUpdate会因为total更新触发组件重新渲染,进而再次执行componentDidUpdate,导致无限循环。
函数组件实现(用useEffect)
如果是用Hook的函数组件,直接用useEffect监听三个sub字段的变化:
import { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; const MyComponent = () => { const sub1 = useSelector(state => state.form.sub1); const sub2 = useSelector(state => state.form.sub2); const sub3 = useSelector(state => state.form.sub3); const total = useSelector(state => state.form.total); const dispatch = useDispatch(); // 当sub1/sub2/sub3变化时,计算总和并更新状态 useEffect(() => { const calculatedTotal = sub1 + sub2 + sub3; dispatch({ type: 'UPDATE_TOTAL', payload: calculatedTotal }); }, [sub1, sub2, sub3, dispatch]); // 依赖数组里必须包含三个sub和dispatch return ( <div> <p>当前总和:{total}</p> </div> ); }; export default MyComponent;
方案2:Redux层面处理派生数据(更符合Redux最佳实践)
其实total是派生数据(由其他状态计算而来),按照Redux的最佳实践,这类数据没必要存在Redux状态里,直接通过memoized selector计算即可,既节省状态空间,又能避免冗余更新。
用reselect创建memoized selector
reselect是Redux官方推荐的用于创建memoized selector的库,只有当依赖的状态变化时,才会重新计算结果:
import { createSelector } from 'reselect'; // 先定义基础的状态选择器 const selectSub1 = state => state.form.sub1; const selectSub2 = state => state.form.sub2; const selectSub3 = state => state.form.sub3; // 创建memoized的总和选择器 export const selectTotal = createSelector( [selectSub1, selectSub2, selectSub3], (sub1, sub2, sub3) => sub1 + sub2 + sub3 ); // 在组件里直接使用这个selector const MyComponent = () => { // 只有当sub1/sub2/sub3变化时,total才会重新计算 const total = useSelector(selectTotal); return ( <div> <p>当前总和:{total}</p> </div> ); };
如果一定要把total存到Redux状态里
如果业务场景必须将total持久化到Redux状态,那可以在reducer里处理:每当sub1/sub2/sub3更新时,自动计算并更新total:
const initialState = { sub1: 0, sub2: 0, sub3: 0, total: 0 }; const formReducer = (state = initialState, action) => { switch (action.type) { case 'UPDATE_SUB1': const newSub1 = action.payload; return { ...state, sub1: newSub1, total: newSub1 + state.sub2 + state.sub3 }; case 'UPDATE_SUB2': const newSub2 = action.payload; return { ...state, sub2: newSub2, total: state.sub1 + newSub2 + state.sub3 }; case 'UPDATE_SUB3': const newSub3 = action.payload; return { ...state, sub3: newSub3, total: state.sub1 + state.sub2 + newSub3 }; default: return state; } };
方案3:用Redux中间件监听action(适合复杂业务场景)
如果你的求和逻辑需要和其他异步操作联动,或者多个组件都需要触发这个计算,那可以用Redux中间件(比如redux-thunk或redux-saga)来监听sub字段的更新action,自动触发总和计算。
用redux-thunk实现
在更新sub字段的action creator里,先更新sub状态,再计算总和并更新total:
// 更新sub1的action creator export const updateSub1 = (value) => (dispatch, getState) => { // 先更新sub1的状态 dispatch({ type: 'UPDATE_SUB1', payload: value }); // 获取最新的状态计算总和 const { sub1, sub2, sub3 } = getState().form; // 更新total状态 dispatch({ type: 'UPDATE_TOTAL', payload: sub1 + sub2 + sub3 }); }; // 同理,updateSub2和updateSub3也可以用同样的方式实现
用redux-saga实现
通过saga监听三个sub字段的更新action,当action触发时自动计算总和:
import { takeLatest, select, put } from 'redux-saga/effects'; // 监听sub字段的更新action function* watchSubUpdates() { yield takeLatest(['UPDATE_SUB1', 'UPDATE_SUB2', 'UPDATE_SUB3'], updateTotalSaga); } // 计算并更新total的saga function* updateTotalSaga() { // 获取当前Redux中的sub状态 const { sub1, sub2, sub3 } = yield select(state => state.form); const total = sub1 + sub2 + sub3; // 触发更新total的action yield put({ type: 'UPDATE_TOTAL', payload: total }); } export default function* rootSaga() { yield watchSubUpdates(); }
内容的提问来源于stack exchange,提问作者Shawn Matthews

