
只有两个方向
React 里组件之间的通信只有两个方向:数据往下传(props),事件往上报(回调函数)。所有距离上的做法都是这两条的组合。
父子:数据下行,事件上行
function SearchInput({ value, onChange }) { return ( <input value={value} onChange={e => onChange(e.target.value)} placeholder="搜索" /> )}
function SearchPage() { const [query, setQuery] = useState('')
return ( <> <SearchInput value={query} onChange={setQuery} /> <ResultList query={query} /> </> )}SearchInput 自己不存状态,它拿到的 value 和 onChange 都由父组件提供。这一层抽象叫受控组件,输入框的一切都由上层决定,父组件因此能在同一处拿到关键词并传给结果列表。受控与非受控的取舍见 受控组件与非受控组件。
子组件永远不改接收到的 props。要改,就把「改成什么」通过回调交回去。
兄弟:把状态提到共同父组件
两个兄弟组件要共享同一份数据时,不要把状态放在其中一方,那会让另一方必须去找它。把状态提到两者的共同父组件,再分别传下去:
function TemperatureInput({ label, value, onChange }) { return ( <label> {label}: <input value={value} onChange={e => onChange(e.target.value)} /> </label> )}
function BoilingVerdict({ celsius }) { const value = parseFloat(celsius) return <p>{value >= 100 ? '水会沸腾' : '水不会沸腾'}</p>}
function Calculator() { const [celsius, setCelsius] = useState('')
return ( <> <TemperatureInput label="摄氏度" value={celsius} onChange={setCelsius} /> <BoilingVerdict celsius={celsius} /> </> )}这是官方所说的「把状态提上去」:Calculator 持有唯一的真值,两个子组件都只是显示与上报。
跨多层:先试组合,再上 Context
数据要从最外层传到第四层时,中间几层都成了搬运工。两条路可以走。
先试组合:让中间层接收 JSX 作为 children,它就不必认识那些数据。
function Layout({ children }) { return <div className="layout">{children}</div>}
function App() { const theme = 'dark'
return ( <Layout> <Nav theme={theme} /> </Layout> )}Nav 由 App 直接渲染,Layout 不必认识 theme。
组合解决不了时再用 Context。创建、提供、读取三步,以及「value 变化会让所有消费者重渲染」这类坑,写在 useContext 一篇里,这里不重复。
全局:先问清楚「全局」是不是真的
跨模块共享的数据(登录态、主题、语言)才值得放进状态库或全局 Context。判断标准是读者能不能一眼看出谁依赖它。
自定义事件总线这类做法要避免。它绕开了 React 的数据流:组件之间不再有可见的依赖关系,一次渲染是谁触发的很难追;订阅与退订还得手工配对,漏一处就是内存泄漏。真需要订阅 React 之外的数据源(localStorage、浏览器 API、第三方 store),走 useSyncExternalStore,把订阅契约写在一个地方。
传下去的东西会决定要不要重渲染
把每次渲染都新建的对象或函数传给被 memo 包起来的子组件,等于把优化击穿:
// 每次渲染都是新对象,子组件的 memo 拦不住<Child config={{ mode: 'compact' }} />
// 放在组件外,或在需要时用 useMemo 固定引用const config = { mode: 'compact' }<Child config={config} />先看有没有必要:子组件没包 memo,或者在简单场景里,直接传字面量更省事。要固定引用时再用 useMemo 与 useCallback。
选择顺序
按距离从近到远选:props 与回调 → 提升状态 → 组合 → Context → 状态库。
越往后的方案,代价越在于「依赖关系变得不可见」。能用近的,就别用远的。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 189 天
部分内容可能已过时