组件树的状态对渲染十分重要!!!
jsx
const VerySlowComponent = () => {
console.log("VerySlowComponent render");
return <div> VerySlowComponent</div>;
};
const ScrollableBlock = ({ children }) => {
const [count, setCount] = useState(0);
console.log("ScrollableBlock render");
return (
<>
{count}
<button onClick={() => setCount((c) => c + 1)}>count++</button>
{children}
</>
);
};
const App = () => {
return (
<ScrollableBlock>
<VerySlowComponent />
</ScrollableBlock>
);
};
// 把setCount逻辑移到ScrollableBlock
//count改变,只有ScrollableBlock重新渲染
把setCount逻辑移到ScrollableBlock,count改变,只有ScrollableBlock重新渲染
jsx
const VerySlowComponent = () => {
console.log("VerySlowComponent render");
return <div> VerySlowComponent</div>;
};
const ScrollableBlock = () => {
const [count, setCount] = useState(0);
console.log("ScrollableBlock render");
return (
<>
{count}
<button onClick={() => setCount((c) => c + 1)}>count++</button>
<VerySlowComponent />
</>
);
};
const App = () => {
return (
<ScrollableBlock>
</ScrollableBlock>
);
};
这样verySlowCompnent会跟着重新渲染
第一种情况(ScrollableBlock 接收 children 作为 props)
javascript
const App = () => {
return (
<ScrollableBlock>
<VerySlowComponent />
</ScrollableBlock>
);
};
为什么 VerySlowComponent 不会重新渲染?
children的稳定性 当ScrollableBlock的children是通过 JSX 直接传递的(如<VerySlowComponent />),children的引用在父组件(App)的渲染中始终保持不变。即使ScrollableBlock内部状态count变化导致自身重新渲染,children属性(即VerySlowComponent)的引用未变,因此 React 会跳过子组件的重新渲染- React 的优化机制 React 在对比新旧
props时,发现children的 JSX 元素未发生任何变化(相同类型、相同 props),因此不会触发子组件的重新渲染
第二种情况(VerySlowComponent 直接内嵌在父组件中)
javascript
const ScrollableBlock = () => {
const [count, setCount] = useState(0);
return (
<>
{count}
<button onClick={() => setCount(c => c + 1)}>count++</button>
<VerySlowComponent />
</>
);
};
为什么 VerySlowComponent 会重新渲染?
- 每次渲染生成新的子组件实例 在
ScrollableBlock的函数体中直接使用<VerySlowComponent />,每次父组件重新渲染时,都会生成一个新的VerySlowComponent实例。React 会认为这是一个新的元素,即使其 props 未变,也会触发重新渲染 - 无
children的引用稳定性保护 此时VerySlowComponent是父组件渲染逻辑的一部分,而非通过children传递。父组件的任何状态变化都会导致其内部所有 JSX 元素的重新创建,包括子组件
核心差异总结
| 场景 | 子组件接收方式 | 引用稳定性 | 是否重新渲染 |
|---|---|---|---|
| 通过 children 传递 | 作为父组件的 props | 引用不变,React 跳过渲染 | 否 |
| 直接内嵌在父组件中 | 父组件渲染逻辑的一部分 | 每次生成新实例,强制渲染 | 是 |