React子组件向父组件更新状态时如何避免父组件重新渲染
核心认知澄清
如果你需要将选中值存储在父组件的state中,调用父组件setState触发父组件重渲染是React的默认机制,不存在更新父组件状态又完全不触发父组件重渲染的方法。但我们可以通过优化将重渲染的开销降到最低,达到和“父组件不重渲染”一致的性能效果。
具体优化方案
方案1:缓存组件与回调(最常用,成本最低)
你现在的性能浪费主要来自两个可优化点:父组件每次重渲染都会生成新的回调函数,导致子组件无意义重渲染;父组件重渲染时所有子组件都会跟着重渲染,即使子组件完全没用到更新的状态。按以下步骤修改即可:
- 父组件用
useCallback缓存传给子组件的回调,保证回调引用稳定 - 子组件用
React.memo包裹,仅当props变化时才触发重渲染 - 父组件内其他不需要用到
trainingTitle状态的子组件,也可用memo包裹避免无意义重渲染
修改后的父组件示例:
import { useCallback } from 'react'; import SearchTraining from '../components/training/SearchTraining'; const Parent = () => { // 用useCallback缓存回调,引用不会随父组件重渲染变化 const SearchTrainingData = useCallback((index) => { setState((prev) => ({ ...prev, trainingTitle: index, })); }, []); // setState引用稳定,此处依赖项可留空,有其他用到的外部变量补到依赖数组即可 return ( <> <SearchTraining SearchTrainingData={SearchTrainingData} /> {/* 其他不需要用到trainingTitle的子组件用memo包裹后,不会随父组件重渲染 */} </> ) }
修改后的子组件示例:
import { memo } from 'react'; // 用memo包裹子组件,仅props变化时才触发重渲染 const SearchTraining = memo((props) => { const onChangeAutoComplete = (key, val) => { console.log(key); props.SearchTrainingData(val); }; // 剩余Autocomplete相关逻辑保持不变即可 return ( <Autocomplete {/* 原有配置不变 */} /> ) })
方案2:状态下沉到子组件(性能最优,符合场景优先用)
如果trainingTitle状态仅需要在Autocomplete子组件内使用,不需要在父组件/其他兄弟组件中调用,直接把这个状态放到子组件内部管理即可,完全不需要和父组件做状态通信,更新状态只会触发子组件重渲染,父组件不会有任何变动。
方案3:跨组件状态共享避免父组件重渲染
如果trainingTitle需要在多个不相关的组件中共享,不想让父组件承担状态容器的角色,可以用轻量状态管理库(比如Zustand、Jotai)实现:状态存在独立的store中,子组件直接调用store的方法更新状态,只有用到该状态的组件会触发重渲染,父组件完全不会被影响。
新手优化建议
- 不要过度追求“零重渲染”,React本身的重渲染性能很高,大部分场景下只要没有无意义的子组件重渲染,完全不需要做额外优化
- 状态遵循“谁用谁管理”的原则,能放在组件内部就不要提升到父组件/全局,能减少很多不必要的状态通信开销
- 传递给子组件的函数、对象类型的props,尽量用
useCallback、useMemo缓存,避免子组件无意义重渲染
内容的提问来源于stack exchange,提问作者Jc John
相关产品推荐
相关产品推荐

