React如何在不触发父组件重渲染的前提下从一个子组件修改另一个子组件的状态
问题根因
你原来的方案失效的核心原因有两个:
- 你在父组件内定义的
setChildOneContent是渲染周期内的局部变量,ChildOne的useEffect会在组件挂载完成(也就是父组件已经完成所有子组件渲染后)才执行赋值,此时传给ChildTwo的setChildOneContent早已经是初始的undefined了 - 就算后续变量被赋值,你也没有触发父组件重渲染来把新值传递给
ChildTwo,而触发父组件重渲染恰恰是你要避免的需求
轻量实现方案
不需要引入任何第三方状态管理库,只用React原生的useRef就能实现,核心逻辑是用不需要触发渲染的ref容器来存储子组件的状态更新函数,修改后完整代码如下:
import React, { Dispatch, SetStateAction, useEffect, useRef, useState, useCallback } from "react"; export default function ChildToChild() { const renderCounter = useRef(0); renderCounter.current = renderCounter.current + 1; // 用ref存储ChildOne的状态更新函数,读写ref不会触发父组件重渲染 const setChildOneContentRef = useRef<Dispatch<SetStateAction<string>> | undefined>(undefined); // 用useCallback包裹保证函数引用稳定,避免ChildOne的useEffect重复执行 const childOneContentController = useCallback( (setter: Dispatch<SetStateAction<string>>) => { setChildOneContentRef.current = setter; }, [] ); return ( <div> <h1>Don't re-render me please</h1> <p>No. Renders: {renderCounter.current}</p> <ChildOne childOneContentController={childOneContentController} /> {/* 直接把ref传给ChildTwo,始终是同一个引用 */} <ChildTwo setChildOneContentRef={setChildOneContentRef} /> </div> ); } function ChildOne({ childOneContentController, }: { childOneContentController: (setter: Dispatch<SetStateAction<string>>) => void; }) { const [content, setContent] = useState("original content"); useEffect(() => { childOneContentController(setContent); }, [childOneContentController, setContent]); return ( <div> <h2>Child One</h2> <p>{content}</p> </div> ); } function ChildTwo({ setChildOneContentRef, }: { setChildOneContentRef: React.MutableRefObject<Dispatch<SetStateAction<string>> | undefined>; }) { return ( <div> <h2>Child Two</h2> <button onClick={() => { // 直接读取ref的当前值调用即可 if (setChildOneContentRef.current) setChildOneContentRef.current("content changed"); }} > Change Child One Content </button> </div> ); }
方案说明
- 调用
ChildOne的状态更新函数只会触发ChildOne本身重渲染,父组件和ChildTwo都不会触发重渲染,完全符合你的需求 - 所有能力都是React原生提供,不需要额外引入任何依赖,bundle体积零负担
- 这个模式可以扩展到任意多的跨组件状态更新场景,只要把需要共享的状态更新函数都存在ref容器里即可
内容的提问来源于stack exchange,提问作者JBaczuk
相关产品推荐
相关产品推荐

