React重新渲染指南:一次性完全理解
这篇文章是《React重渲染指南:一切都一次性处理》的翻译文章。
我已经得到了本人(Nadia Makarevich女士)的许可来翻译。
以下是翻译文章。
React再渲染指南:一次全面理解
这是一份详细介绍React重新渲染的指南。
在这个指南中,会解释以下内容。
-
- 再レンダリングとは何か?
-
- 必要な再レンダリングと不要な再レンダリングとは何か?
- 何がReactコンポーネントの再レンダリングをトリガーするのか?
另外,还包括以下内容。
-
- 再レンダリングを防ぐための最も重要なパターン
- 不要な再レンダリングと、パフォーマンスの低下につながるアンチパターン
所有的模式和反模式都附有图示和代码示例。
该文章的一些章节也可以通过视频观看。
https://www.youtube.com/channel/UCxz8PLj1ld6y-zpJmpqpOrw
目录
-
- 什么是React的重新渲染?
-
- React组件会在什么时候重新渲染?
-
- 使用组合来防止重新渲染
-
- 使用React.memo来防止重新渲染
-
- 使用useMemo/useCallback来提高重新渲染性能
-
- 提高列表的重新渲染性能
- 使用Context来防止引起的重新渲染
1. 重新渲染是指什么?
这一章节也可以通过视频观看。
当谈论React的性能时,需要注意两个阶段。
初期レンダリング – コンポーネントが最初に画面に表示されるときに発生するレンダリングのこと
再レンダリング – すでに画面に表示されているコンポーネントで発生する2回目以降のレンダリングのこと
当React使用新数据更新应用程序时,会发生重新渲染。通常情况下,重新渲染是由用户操作应用程序,或者通过异步请求或订阅模式操作获取的外部数据所引起的。
在不存在非同步数据更新的非交互式应用程序中,重新渲染绝对不会发生。因此,不需要考虑对这类应用程序进行重新渲染的性能优化。
? 什么是必要的重新渲染和不必要的重新渲染?
必要な再レンダリング是指原始组件或直接使用新信息的组件的重新渲染。例如,如果用户在输入表单中输入了任何内容,管理该状态的组件需要在用户按下按键时进行自我更新。这就是所需的再渲染。
这是由于错误且低效的应用程序设计导致的各种不必要的再渲染在整个应用程序中传播的再渲染。例如,当用户在输入字段中输入内容并按下键盘时,整个页面都会重新渲染,这样页面就会被不必要地重新渲染。
不需要的重新渲染本身并不是一个问题。由于React非常快速,大多数情况下,用户不会注意到它。
然而,如果重新渲染发生过多或在非常重的组件上执行,用户体验可能会出现”卡顿”或”延迟”的现象,每次操作都会明显延迟,甚至可能导致应用程序完全无响应。
2. React组件何时重新渲染?
这一篇章的内容可以通过视频进行观看。
组件重新渲染的原因有四个。
-
- 状态变更
-
- 父组件(或子组件)重新渲染
-
- 上下文变更
- React hooks的修改
另外,关于组件重新渲染的原因,也存在一些误解。
- コンポーネントの props が変更されると再レンダリングが発生する
这就是事实。这并不是事实(参考以下说明)。
? 再渲染的原因是状态的改变
当组件的状态发生变化时,该组件将重新渲染。
通常情况下,这会在回调函数或 useEffect 钩子中发生。
状态的改变是所有重新渲染的“根源”原因。
请参考CodeSandbox的示例
const Component = () => { // 2. 再レンダリング
const [state, setState] = useState(); // 1. 状態の変更
return ...
};
? 重新渲染的原因:父组件的重新渲染
当组件的父组件重新渲染时,该组件本身也会重新渲染。换句话说,当组件重新渲染时,该组件的所有子组件也会重新渲染。
组件的重新渲染总是向下方向遍历组件树。
也就是说,子组件的重新渲染不会触发父组件的重新渲染。(但有一些注意事项和特殊情况,详细信息请参考下一篇指南:《React 元素、子组件、父组件和重新渲染的奥秘》)。
请参考 codesandbox 的示例。
const Parent = () => { // 1. 再レンダリング
return <Child /> // 2. 再レンダリング
};
? 再渲染的原因:上下文的变更
当Context API的Context Provider的值发生变化时,使用该Context的所有组件都会重新渲染,即使它们并没有直接使用变化的数据部分。虽然不能通过记忆化来阻止这些重新渲染,但可以采用一些近似的方法来避免(参见Part 7: preventing re-renders caused by Context)。
请参考 CodeSandbox 上的示例
const useValue = useContext(Context) // 1. Context の値の変更
const Component1 = () => {
const value = useValue(); // 2. 再レンダリング
return ...
};
const Component2 = () => {
const value = useValue(); // 2. 再レンダリング
return ...
};
? 重新渲染的起因:React hooks 的改变
在 Hook 内发生的一切都 “属于” 使用它的组件。
在这里也适用于 “上下文更改” 和 “状态更改” 的规则。
-
- フック内の状態の変更は、”ホスト”コンポーネント(フックを使用しているコンポーネント)の再レンダリングをトリガーします。これは 防ぎようがありません 。
- フックが Context を使用し、Context の値が変更されると、”ホスト”コンポーネントの再レンダリングをトリガーします。これは 防ぎようがありません 。
钩子可以链接(在一个钩子内使用另一个钩子)。即使被链接,每个钩子都“属于”主机组件,并且同样的规则适用于所有的钩子。
请参考 Codesandbox 的示例。
const useSomething = useContext(Context) // 1. Context の値の変更
const useValue = {
useSomething() // 2. チェーンが反応
}
const Component = () => { // 3. 再レンダリング
const value = useValue();
return ...
};
再渲染的原因:更改了props(犯了一个大错误)。
不论组件的 props 是否被改变,对于未被存储的组件重新渲染并不相关。
要更改 props 的话,必须通过父组件进行更新。也就是说,必须重新渲染父组件。这将触发子组件的重新渲染。与 props 无关。
请参考 codesandbox 的示例
只有在使用记忆技术(React.memo、useMemo)时,props的更改才变得重要。
const Parent = () => { // 1. 再レンダリング
return <Child value={{ value }} /> // 2. 再レンダリング(props の `value` は関係なし)
};
使用组合来防止再渲染。
这一章也可以在视频中观看。
反模式:在render函数中创建组件
在另一个组件的渲染函数内创建一个组件是一个导致性能严重下降的反模式。
每次重新渲染时,React会重新挂载该组件(即销毁组件并重新创建)。这比普通的重新渲染要慢得多。此外,这可能导致以下类型的错误。
-
- 再レンダリング中にコンテンツが「フラッシュする」可能性がある
-
- 再レンダリングのたびにコンポーネント内の状態がリセットされる
-
- 依存関係のない useEffect が再レンダリングのたびにトリガーされる
- コンポーネントがフォーカスされていた場合、フォーカスが失われる
请参考 Codesandbox 上的示例
如何编写高性能的React代码: 规则、模式、要做和不要做的事项
不好的
const Component = () => { // 1. 再レンダリング
const SlowComponent = () => <Something />; // 2. 再作成されたコンポーネント
return (
<SlowComponent /> // 3. 再マウント!!
)
}
好的
const SlowComponent = () => <Something />; // 2. (再マウントされない)同じコンポーネントのまま
const Component = () => { // 1. 再レンダリング
return (
<SlowComponent /> // 3. 再レンダリングされるだけ
)
}
✅ 使用组合来防止重渲染:将状态移到下层(子组件)。
这一章节也可以在视频中观看。
这种模式适用于重型组件管理状态,而且该状态仅在渲染树的一部分中使用的情况。一个典型的例子是,在渲染页面的复杂组件中,点击按钮来打开或关闭对话框。
在这种情况下,可以将模态对话框的显示状态、对话框本身以及触发更新的按钮封装到较小的组件中。通过这样做,即使这些状态发生变化,大型组件也不会重新渲染。
请参考 CodeSandbox 上的示例。
React元素的奥秘:子元素、父元素和重新渲染之谜,编写高性能的React代码:规则、模式、要做的和不该做的事情。
糟糕
const Component = () => {
const [open, setOpen] = useState(false); // 1. 再レンダリングをトリガーする
return (
<Something>
<Button onClick={() => setOpen(true)} /> {/* 1. 再レンダリングをトリガーする */}
{isOpen && <ModalDialog />}
<VerySlowComponent /> {/* 2. 再レンダリング */}
</Something>
);
};
好
const ButtonWithDialog = () => {
const [open, setOpen] = useState(false); // 1. 再レンダリング
return (
<>
<Button onClick={() => setOpen(true)} /> {/* 1. 再レンダリング */}
{isOpen && <ModalDialog />}
</>
);
};
const Component = () => {
return (
<Something>
<ButtonWithDialog />
<VerySlowComponent /> {/* 2. 影響を受けない */}
</Something>
);
};
通过使用组合防止重新渲染:将其作为 props 中的 children。
你可以在视频中观看本章内容。
这个模式也可以被称作“将状态包装在children周围”。它类似于“将状态下移给子组件”的模式,可以将状态的变化封装在更小的组件中。不同之处在于,状态被用于包装渲染树中的慢速元素,所以提取它并不那么容易。一个典型的例子是在组件的根元素上附加的onScroll或onMouseMove回调函数。
在这种情况下,我们可以将状态管理和使用该状态的组件提取为较小的组件,将慢速组件作为该组件的子组件传递是一个不错的选择。从较小的组件的角度来看,子组件只是简单的props,不会受到状态更改的影响。换句话说,不会发生重新渲染的情况。
请参考CodeSandbox的示例。
追加资源:React元素、子元素、父元素和重新渲染的谜团
不好
const Component = () => {
const [value, setValue] = useState({}); // 1. 再レンダリングをトリガーする
return (
<div onScroll={(e) => setValue(e)}> {/* 1. 再レンダリングをトリガーする */ }
<VerySlowComponent /> {/* 2. 再レンダリング */}
</div>
);
};
好的 de)
const ComponentWithScroll = () => {
const [value, setValue] = useState({}); // 1. 再レンダリングをトリガーする
return (
<div onScroll={(e) => setValue(e) }> {/* 1. 再レンダリングをトリガーする */ }
{children} {/* 2. 単なる props なので、影響を受けない */}
</div>
);
};
const Component = () => {
return (
<ComponentWithScroll>
<VerySlowComponent /> {/* 3. 影響を受けない */}
</ComponentWithScroll>
);
};
✅ 使用组件防止重新渲染: 将其作为属性传递给组件。
这一章节可在视频中观看。
这个新模式与之前的模式几乎相同,其功能也相同。它将状态封装在小组件内部,并将重型组件作为 props 传递。由于 props 不会受到状态变化的影响,因此重型组件将不会重新渲染。
当一些重要的组件与状态无关时,它们无法被视为类似于children的群组,这时非常方便。
请参考 CodeSandbox 上的示例。
有关将组件作为 props 传递的方法,请参阅以下详细内容:React 组件作为 prop 的正确方法™️、React 元素、子组件、父组件和重新渲染的神秘之处。
糟糕
const Component = () => {
const [value, setValue] = useState({}); // 1. 再レンダリングをトリガーする
return (
<div onScroll={(e) => setValue(e)}> {/* 1. 再レンダリングをトリガーする */ }
<SlowComponent1 /> {/* 2. 再レンダリング */}
<Something /> {/* 2. 再レンダリング */}
<SlowComponent2 /> {/* 2. 再レンダリング */}
</div>
);
};
好
const ComponentWithScroll = ({ left, right }) => {
const [value, setValue] = useState({}); // 1. 再レンダリングをトリガーする
return (
<div onScroll={(e) => setValue(e) }> {/* 1. 再レンダリングをトリガーする */ }
{left} {/* 2. 単なる props なので、影響を受けない */}
<Something />
{right} {/* 2. 単なる props なので、影響を受けない */}
</div>
);
};
const Component = () => {
return (
<ComponentWithScroll>
left={<SlowComponent1 />} {/* 2. 影響を受けない */}
right={<SlowComponent2 />} {/* 2. 影響を受けない */}
/>
);
};
用 React.memo 避免重新渲染。
这一节的内容你也可以在视频中观看。
当使用 React.memo 来包装组件时,只要该组件的 props 没有改变,就可以在渲染树的上层阻止下游触发的重新渲染链。
当渲染重型组件时,它对于源(即状态或已更改的数据)的再渲染是不依赖的。
请参考 CodeSandbox 的示例。
不好 (bù
const Parent = () => { // 1. 再レンダリング
return <Child /> // 1. 再レンダリング
};
const ChildMemo = React.memo(Child);
const Parent = () => { // 1. 再レンダリング
return <ChildMemo /> // 2. 再レンダリングされない
};
✅ React.memo:具有 props 的组件
为了使React.memo起作用,必须将所有非原始值的props进行记忆化。
请参考Codesandbox的示例。
坏的
const ChildMemo = React.memo(Child);
const Parent = () => { // 1. 再レンダリング
return (
<ChildMemo
value={{ value }} // 2. `value` が変更され、再レンダリングされる
/>
)
};
好
const ChildMemo = React.memo(Child);
const Parent = () => { // 1. 再レンダリング
const value = useMemo(() => ({ value }), []) // 2. `value` は変更されない
return (
<ChildMemo
value={value} // 3. 再レンダリングされない
/>
)
};
✅ React.memo:作为props或子组件
React.memo 必须在作为子项和属性传递的元素上使用。
即使对父组件进行记忆化,也无效。由于子项和属性是对象,因此每次重新渲染时都会发生变化。
有关React元素、子节点、父节点和重新渲染的神秘之处的详细说明,请参考这里的说明:React元素、子节点、父节点和重新渲染的奥秘之谜。
请参考 CodeSandbox 上的示例。
差劲的
const ChildMemo = React.memo(Child);
const Parent = () => { // 1. 再レンダリング
return (
<ChildMemo left={
<Something /> {/*
2. 再レンダリング */}
}>
<GrandChild /> {/*
2. 再レンダリング */}
</ChildMemo>
)
}
好
const SomethingMemo = React.memo(Something);
const GrandChildMemo = React.memo(GrandChild);
const Parent = () => { // 1. 再レンダリング
return (
<Child left={
<SomethingMemo /> {/*
2. 再レンダリングされない */}
} />
<GrandChildMemo /> {/*
2. 再レンダリングされない */}
</Child>
)
}
使用useMemo/useCallback来提高重新渲染的性能
⛔️反模式: 对于 props 不必要的 useMemo/useCallback
即使将props进行记忆化,也无法防止子组件的重新渲染。
只要父组件重新渲染,不论与props是否相关,都会触发子组件的重新渲染。
请参考CodeSandbox上的示例。
const Parent = () => { // 1. 再レンダリング
const value = useMemo(() => ({ value }))
return (
<Child
value={value} {/* 関係ない */}
/> {/* 2. 再レンダリング */}
};
✅ 使用 useMemo/useCallback 是必要的。
如果子组件被 React.memo 包装,那么需要对所有非原始值的 props 进行记忆化。
请参考 CodeSandbox 的示例。
const ChildMemo = React.memo(Child)
const Parent = () => { // 1. 再レンダリング
const value = useMemo(() => ({ value })
return (
<ChildMemo {/* 3. 関係なし */}
value={value} {/* 2. メモ化する必要がある */}
/>
};
如果组件中使用了像 useEffect、useMemo、useCallback 这样的钩子,并且使用了非原始值作为依赖关系,那么它们应该进行记忆化处理。
请参考 CodeSandbox 上的示例。
const Parent = () => { // 1. 再レンダリング
const value = useMemo(() => ({ value }))
useEffect(() => {
// do something
}, [value]) // 2. メモ化する必要がある
return ...
};
✅ 为高成本的计算使用 useMemo
使用useMemo的一个用例是避免在每次重新渲染时执行高成本的计算。
然而,由于useMemo具有一些开销(会消耗一些内存,使初始渲染稍微变慢),因此并不应该在所有的计算中使用。在React中,组件的挂载和更新通常是最耗费性能的计算(除非实际上需要在前端进行素数计算等情况)。
useMemo 的典型用例是对 React 元素进行记忆化。React 元素通常是对现有渲染树的一部分进行回传,或者是返回新元素的 `map` 函数生成的渲染树结果。
通常情况下,对于JavaScript的“纯净”操作,如数组的排序或过滤等,与组件更新相比,其代价往往可以忽略不计。
请参考 CodeSandbox 上的示例。
坏的 de)
const Component = () => { // 1. 再レンダリング
return (
<>
<Something />
<SlowComponent /> {/* 2. 再レンダリング */}
<SomethingElse />
</>
);
};
好
const Component = () => { // 1. 再レンダリング
const slowComponent = useMemo(() => {
return <SlowComponent /> {/* 2. 再レンダリングされない */}
}, [])
return (
<>
<Something />
{slowComponent} {/* 2. 再レンダリングされない */}
<SomethingElse />
</>
);
};
优化列表重新渲染的性能。
这一章的内容也可以在视频中观看。
你可以在以下链接中观看:https://youtu.be/76OedwmXlYY。
除了常规的重渲染规则和模式外,key属性还会影响React列表的性能。
重要提示:仅仅指定key属性不能提高列表的性能。为了防止列表元素重新渲染,需要用React.memo来包裹列表元素。
关键的值应该是一个在重新渲染之间保持一致的字符串,适用于列表中的所有元素。通常情况下,这会使用列表项的 id 或者数组的索引来实现。
如果列表是静态的(无法添加/删除/插入/排序元素),那么使用数组的索引作为键是可以的。
使用动态列表来操作数组的索引会导致以下问题:
-
- リストアイテムが状態を持っていたり(フォーム入力のような)非制御要素を含む場合、バグが発生します
- リストアイテムが React.memo でラップされている場合、パフォーマンスが低下します
请参考「React key属性:优化列表性能的最佳实践」以获取详细信息。
请参考静态列表示例中的 codesandbox。
请参考 CodeSandbox 上的动态列表示例。
坏
const Component = () => { // 1. 再レンダリング
return (
<>
{items.map((item) => (
<Child
item={item}
key={item.id} {/* 3. 再レンダリングを防げない */}
/> {/* 2. 再レンダリング */}
))};
</>;
);
};
好
const ChildMemo = React.memo(Child)
const Component = () => { // 1. 再レンダリング
return (
<>
{items.map((item) => (
<ChildMemo
item={item}
key={item.id}
/> {/* 2. 再レンダリングされない */}
))};
</>;
);
};
⛔️ 反模式:使用随机值作为列表的键
请不要将随机生成的值用作列表的 key 属性值。这将导致 React 在重新渲染时重新挂载列表项,并可能导致以下情况发生。
-
- リストのパフォーマンスが非常に悪くなります
- リストアイテムが状態を持っていたり(フォーム入力のような)非制御要素を含む場合、バグが発生します
请参考CodeSandbox的示例。
坏的
const ChildMemo = React.memo(Child)
const Component = () => { // 1. 再レンダリング
return (
<>
{items.map((item) => (
<ChildMemo
item={item}
key={Math.random()} {/* 2. レンダリングされるたびに再マウントされてしまう */}
/> {/* 2. 再マウント!! */}
))}
</>
)
}
根据上下文防止发生重新渲染。
防止重新渲染上下文:提供者值的记忆化
如果Context API的Context Provider没有放置在应用程序的根组件中,并且Context Provider的祖先组件发生变化导致Context Provider本身重新渲染,那么需要对其值进行存储缓存。
请参考 codesandbox 上的示例
坏
const Component = () => { // 1. 再レンダリング
return (
<Context.Provider
value={{ value }} {/* 2. `value` が変更され */}
> {/* 2. すべてが再レンダリングされる! */}
{children}
</Context.Provider>
);
};
好的。 de.)
const Component = () => { // 1. 再レンダリング
const memoValue = useMemo(() => ({ value }), [])
return (
<Context.Provider
value={memoValue} {/* 2. 同じ値のまま */}
> {/* 3. 何も再レンダリングされない! */}
{children}
</Context.Provider>
);
};
防止重新渲染上下文:数据与API的分离
如果在上下文中有数据和API(getter和setter)的组合,可以将它们拆分为同一个组件中的不同提供者。通过这样做,仅使用API的组件将不再在数据变更时重新渲染。
关于这种模式的详细信息,请参阅此处:如何使用Context编写高性能的React应用程序
请查看 CodeSandbox 的示例。
糟糕的
const Component = () => {
const [state, setState] = useState(); // 1. 状態が変更される
const value = useMemo(
() => ({
data: state,
api: (data) => setState(data),
}),
[state],
);
return (
<Context.Provider value={
{ value }
}> {/* 2. すべての Consumer が再レンダリングされる */}
{children}
</Context.Provider>
);
};
好
const Component = () => {
const [state, setState] = useState(); // 1. 状態が変更される
return (
<DataContext.Provider value={state}>
<ApiContext.Provider
value={setState}
> {/* 2. API の Consumer は再レンダリングされない */}
{children}
</ApiContext.Provider>
</DataContext.Provider>
);
};
✅ 防止上下文重新渲染:将数据分割为块
当上下文中存在多个独立的数据时,可以在同一个提供程序内将它们分割为小的提供程序。通过这样做,可以仅重新渲染已更改数据的使用者。
关于这种模式的详细信息,请查看这里:如何使用Context编写高性能的React应用程序
请参考Codesandbox上的示例。
糟糕的 de)
const Component = () => {
const [first, setFirst] = useState(); // 1. first が変更される
const [second, setSecond] = useState();
const value = useMemo(
() => ({
first: first,
second: second,
}),
[first, second],
);
return (
<Context.Provider value={
{ value }
}> {/* 2. すべての Consumer が再レンダリングされる */}
{children}
</Context.Provider>
);
};
好
const Component = () => {
const [first, setFirst] = useState(); // 1. first が変更される
const [second, setSecond] = useState();
return (
<Data1Context.Provider value={frist}>
<Data2Context.Provider
value={second}
> {/* 2. second の Consumer は再レンダリングされない */}
{children}
</ApiContext.Provider>
</DataContext.Provider>
);
};
防止上下文重新渲染的问题:上下文选择器(选择上下文部分的值)
无法防止只使用上下文的某些值的组件重新渲染。
即使使用了useMemo钩子,数据没有被更改也是一样的。
但是,通过使用高阶组件和 React.memo,可以实现类似于上下文选择器(仅选择上下文的部分值)的功能。
请点击查看关于这个模式的详细内容:React Hooks 时代的高阶组件
请参考 CodeSandbox 上的示例。
不好的。
const useSomething = () => {
const { something } = useContext(Context); // 1. "something" が変更されていなくても、再レンダリングをトリガーする
return useMemo(() => something, [something]); // 2. useMemo は意味なし
};
const Component = () => {
const { something } = useSomething(); // 3. "something" が変更されていなくても、再レンダリングをトリガーする
return ...
};
好的
const withSomething = (Component) => {
const MemoComponent = React.memo(Component); // 1. Component がここでメモ化される
return () => {
const { something } = useContext(Context);
return <MemoComponent something={something} /> // 2. "something" が変更された時だけ再レンダリングされる
}
};
const Component = withSomething(( { something }) => { // 3. "something" が変更された時だけ再レンダリングされる
return ...
};
翻譯者的後記
在参与使用React进行开发时,我开始探索良好的组件设计,而不仅仅是关注重新渲染。在这个过程中,我遇到了这篇博客文章。
这篇文章总结了一些技巧,以紧凑的方式防止进行各种重渲染。但是,最基本的React组件设计原则可能是正确使用”组合”。
我认为”组合”是面向对象编程中经常与”继承”一起讨论的概念。继承是一个”is-a”关系(猫 “是一个” 动物,自行车 “是一个”车辆),而组合是一个”has-a”关系(对话框 “有一个” 按钮,自行车 “有一个” 轮子),在比继承更多地使用组合来提高代码的可重用性的语境中,我们可以看到这个词。
在这篇文章中,介绍了以下3种使用构图技巧的方法。
-
- moving state down(状態を下に移動する)
-
- childrend as props(props としての children)
- components as props(props としての component)
在各种文章中,我们可以找到对于相似技巧的推荐。
优化React重新渲染的一个简单技巧- Kent C. Dodds
将昂贵的组件“提升”到一个不经常被渲染的父组件中。
然后将昂贵的组件作为属性传递。
在你备忘录之前,过度反应了。
下移状態
提升内容
材质界面设计
包裹组件
组件属性
组合 vs 继承 – legacy.reactjs.org
有些组件在接收到子元素之前事先不知道它们的数量。在这种情况下,可以通过使用一个名为 `children` 的特殊 prop 来输出接收到的子元素。
…
通过向通用组件传递 props 并进行设置,可以创建更专用的组件。
…
通过 props 和组合,可以获得足够灵活性,以明确且安全地自定义组件的外观和行为。
在使用上下文之前 – 使用上下文深入传递数据 – react.dev
首先,让我们从传递道具的方法开始。
…
考虑将组件提取出来,将children作为JSX传递的方法。
如果您发现在中间组件中无需使用某些数据,只需要将其向下传递,并且在多个层次上进行传递,那么可能是因为您忘记了提取某个组件。例如,可能会将数据(如post)直接传递给不直接使用数据的可视化组件,例如。相反,让我们将Layout作为props接收children,并尝试以的方式进行渲染。这样可以减少指定数据组件和需要该数据组件之间的层次数。
如果这些方法都行不通的话,请考虑使用上下文。
如果今后要参与React的开发,是否可以考虑将出现的关键词”组合”、”属性”和”子组件”放在心里,来面对组件设计呢?