组件树的状态对渲染十分重要!!!
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 跳过渲染 | 否 |
直接内嵌在父组件中 | 父组件渲染逻辑的一部分 | 每次生成新实例,强制渲染 | 是 |