React Redux中store.subscribe方法出现指数级触发是什么原因?
问题原因
- 你的
store.subscribe直接写在组件函数体顶层,React组件每次重渲染都会执行整个函数体的代码,意味着每渲染一次就会新增一个订阅回调,旧的订阅没有被销毁会一直保留。 - 订阅回调内部调用了
setOpen、setData两个状态更新方法,这两个方法执行后会触发组件重渲染,又会新增一次订阅,最终订阅回调的数量会随着store变更的次数不断累积,每次store触发更新时所有已注册的回调都会执行,就出现了你看到的打印次数越来越多的现象。 - 该现象不是
subscribe的内部特性,属于不符合规范的使用方式导致的问题。
修复方案
- 把订阅逻辑包裹在
useEffect钩子中,并且在effect的返回函数中调用订阅返回的销毁方法,避免回调累积,参考代码如下:
function Alert(){ console.log("rendering"); const [open, setOpen] = useState(false); const [data, setData] = useState(null); const store = useStore(); const dispatch = useDispatch(); useEffect(() => { const unsubscribe = store.subscribe(() => { console.log(store.getState().alerts); const alerts = store.getState().alerts; if (alerts != null) { setOpen(true); setData({ ...alerts }); } else { setData(null); } }); // 组件卸载或effect重新执行前销毁旧订阅 return unsubscribe; }, [store]); // ... }
- 额外优化建议:如果你使用的是Redux框架,不需要手动调用
store.subscribe,直接使用官方提供的useSelector钩子订阅状态即可,它会自动处理订阅的注册和销毁逻辑,代码更简洁也不会出现类似问题:
// 替代手动subscribe的写法 const alerts = useSelector(state => state.alerts); useEffect(() => { if (alerts != null) { setOpen(true); setData({...alerts}); } else { setData(null); } }, [alerts])
内容的提问来源于stack exchange,提问作者Notorious776
相关产品推荐
相关产品推荐

