DrillLab
第 63 / 105 道63 / 105 · #328

组件之间怎么通信

Communication between components

先自己答,再往下看Answer it yourself first

一句话:五种,按「距离」从近到远选。

  • 父 → 子:props。
  • 子 → 父: 父把回调函数当 props 传下去, 子调用它。「事件往上报」就是这个。
  • 兄弟之间状态提升到共同父级, 再分别往下传(#345)。
  • 跨很多层Context—— 适合主题、当前用户、语言这种 「整棵树都要读、又不常变」的值。
  • 全局、复杂、多处修改: 状态库(Redux / Zustand / Jotai) 或服务端状态库(TanStack Query)。

还有两个偏门但会问的:ref +useImperativeHandle(父组件主动调子组件的方法, 比如 focus()play()); 以及 props.children(组合优于配置,也是解决 props drilling 的一招)。

会追问:「什么时候该上状态库?」—— 判据:同一份状态被很多不相关的组件读写、 或者需要时间旅行调试 / 中间件只是「层数深」不该上 Redux, Context 或组合就够—— 这个回答比「大项目就用 Redux」好得多。

In one line: five ways, and you pick by distance — nearest first.

  • Parent → child: props.
  • Child → parent: the parent passes a callback down as a prop and the child calls it. That is all “events go up” means.
  • Between siblings: lift the state to their common parent and pass it back down to each one (#345).
  • Across many levels: Context — good for theme, current user, language: values the whole tree reads and that rarely change.
  • Global, complex, written from many places: a state library (Redux / Zustand / Jotai) or a server-state library (TanStack Query).

Two less common ones they still ask about: ref plus useImperativeHandle (the parent calls a method on the child, say focus() or play()); and props.children (composition over configuration, which is also one answer to props drilling).

Follow-up: “When should you reach for a state library?” — the test: one piece of state is read and written by many unrelated components, or you need time-travel debugging or middleware. Depth alone is no reason for Redux — Context or composition is enough — that answer beats “big project, use Redux” by a mile.

JSX两种最常用的方式示意Illustrative
1// 子 -> 父:传回调下去
2function Parent() {
3 const [text, setText] = useState("");
4 return <Child onChange={setText} />; // 父给回调
5}
6function Child({ onChange }) {
7 return <input onChange={(e) => onChange(e.target.value)} />;
8}
9
10// 用 children 组合,避免中间层被迫透传
11<Layout sidebar={<Nav />}>
12 <Article /> {/* Layout 不需要知道 Article 要什么 props */}
13</Layout>
1// Child to parent: pass a callback down
2function Parent() {
3 const [text, setText] = useState("");
4 return <Child onChange={setText} />; // the parent supplies the callback
5}
6function Child({ onChange }) {
7 return <input onChange={(e) => onChange(e.target.value)} />;
8}
9
10// Compose with children, so the middle layer is not forced to pass things through
11<Layout sidebar={<Nav />}>
12 <Article /> {/* Layout does not need to know what props Article wants */}
13</Layout>