React.memo结合Redux问题:点击子组件无法获取最新stateA
解决方法:避免闭包捕获旧值,同时保持Child组件的渲染优化
嘿,这个问题的核心是闭包捕获了旧的stateA值,咱们来一步步搞定它,同时满足你的两个核心需求:Child只在stateB变更时重渲染,点击Child时能拿到最新的stateA。
问题原因分析
当stateA更新后,Parent组件会重新渲染,但因为你的Child组件用了React.memo,只比较text(也就是stateB)是否变化,所以只要stateB没改,Child就不会重新接收新的handleClick函数。而旧的handleClick是在Parent上一次渲染时创建的,它闭包捕获了当时的props.stateA,所以点击时打印的就是旧值。
方案一:用Ref保存最新的stateA(推荐)
我们可以用React的useRef来保存最新的stateA,这样即使handleClick的引用不变,也能在调用时拿到最新的stateA值,同时不会触发Child的重渲染:
import React from 'react'; import { connect } from 'react-redux'; function Parent(props){ // 创建ref来保存最新的stateA const stateARef = React.useRef(props.stateA); // 每次props.stateA更新时,同步更新ref的值 React.useEffect(() => { stateARef.current = props.stateA; }, [props.stateA]); // 用useCallback让handleClick的引用永远不变 const handleClick = React.useCallback(() => { console.log(stateARef.current); // 这里取的是最新的stateA }, []); return <div><Child text={props.stateB} handleClick={handleClick} /></div> } const mapStateToProps = (state) => { return { stateA: state.stateA, stateB: state.stateB }; }; export default connect(mapStateToProps)(Parent); function Child(props) { return <div onClick={props.handleClick}>{props.text}</div> } export default React.memo(Child,(prev, next) => { return prev.text === next.text });
为什么这个方法有效?
stateARef会在每次stateA更新时同步最新值,而且ref的更新不会触发组件重渲染useCallback让handleClick的引用始终不变,所以即使stateA变化,Child的handleClickprops也不会变,结合React.memo的比较逻辑,Child不会重渲染- 点击时,
handleClick读取的是stateARef.current,也就是最新的stateA值
方案二:直接从Redux Store获取最新值(适合有全局Store实例的情况)
如果你的项目中导出了Redux Store的实例,也可以直接在handleClick里获取最新的state,这样连stateA都不用放进mapStateToProps里了:
import React from 'react'; import { connect } from 'react-redux'; import store from './your-store-path'; // 导入你的Store实例 function Parent(props){ const handleClick = React.useCallback(() => { const latestState = store.getState(); console.log(latestState.stateA); // 直接取最新的stateA }, []); return <div><Child text={props.stateB} handleClick={handleClick} /></div> } // 现在mapStateToProps只需要返回stateB const mapStateToProps = (state) => { return { stateB: state.stateB }; }; export default connect(mapStateToProps)(Parent); // Child组件保持不变 function Child(props) { return <div onClick={props.handleClick}>{props.text}</div> } export default React.memo(Child,(prev, next) => { return prev.text === next.text });
这个方法更简洁,因为不需要把stateA作为props传给Parent,也就完全避免了闭包捕获旧值的问题,同时handleClick的引用不变,Child的渲染优化依然有效。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

