在数据没有发生变化的情况下React组件会进行数次重复渲染,绘制出来完全相同的两个图
寻找子组件重渲染原因实验
测试一:在子组件的props未发生任何变更的情况下是否会发生重新渲染
import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Chart() {
console.log("父组件重新渲染了")
const [selectedOption, setSelectedOption] = useState("option1")
function handleSelectChange(value) {
setSelectedOption(value)
}
return (
<div>
<Select defaultValue={selectedOption} onChange={handleSelectChange}>
<Option value="option1">Option 1</Option>
<Option value="option2">Option 2</Option>
<Option value="option3">Option 3</Option>
</Select>
<ChildComponent />
</div>
)
}
function ChildComponent() {
console.log("ChildComponent重新渲染了")
return <div>Hello from ChildComponent</div>
}
测试结论:子组件即使不接收任何props当父组件重新渲染时子组件也会重新渲染
使用React.memo,关于memo介绍如下:
包装一个组件memo
以获得该组件的记忆版本。只要组件的 props 没有改变,当它的父组件重新渲染时,组件的这个记忆版本通常不会被重新渲染。
但 React 可能仍然会重新渲染它:memoization 是一种性能优化,而不是保证。
import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Chart() {
console.log("父组件重新渲染了")
const [selectedOption, setSelectedOption] = useState("option1")
function handleSelectChange(value) {
setSelectedOption(value)
}
return (
<div>
<Select defaultValue={selectedOption} onChange={handleSelectChange}>
<Option value="option1">Option 1</Option>
<Option value="option2">Option 2</Option>
<Option value="option3">Option 3</Option>
</Select>
{/* */}
<MemoChild />
</div>
)
}
function ChildComponent() {
console.log("ChildComponent重新渲染了")
return <div>Hello from ChildComponent</div>
}
const MemoChild = React.memo(ChildComponent)
即使使用memo,但其仍然会在其接受的props发生变更时及其所在的上下文发生变更时重新渲染。
因此为了更好的使用memo,应当尽量减少props的变化。具体方案如下:
useCallback
在重新渲染之间缓存其定义。例如:当props为对象时,应当减少props对象的变化
接下来进行另外的测试,将子组件放在Adapt中称为内部组件
可以发现,此时子组件的缓存失效了,尽管子组件没有props的变化
但每次父组件重新渲染子组件仍然会重新渲染
import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Adapt() {
console.log("父组件重新渲染了")
const [selectedOption, setSelectedOption] = useState("option1")
function handleSelectChange(value) {
setSelectedOption(value)
}
return (
<div>
<Select defaultValue={selectedOption} onChange={handleSelectChange}>
<Option value="option1">Option 1</Option>
<Option value="option2">Option 2</Option>
<Option value="option3">Option 3</Option>
</Select>
{/* */}
<MemoChild />
</div>
)
}
function ChildComponent() {
console.log("ChildComponent重新渲染了")
return <div>Hello from ChildComponent</div>
}
const MemoChild = React.memo(ChildComponent)
React.memo
失效的原因是因为将 ChildComponent
和 MemoChild
定义在了 Adapt
组件的内部。这意味着每当 Adapt
组件重新渲染时,ChildComponent
和 MemoChild
都会重新定义,导致了缓存失效。
改写测试用例,测试当父组件的props变更时,子组件是否重新渲染
import React, { useState } from "react"
import { Select } from "antd"
const { Option } = Select
export function Adapt() {
console.log("Adapt重新渲染了")
const [filterList, setFilterList] = useState(null)
function onRefresh(value) {
setFilterList(value)
}
return (
<div>
<Chart {...{ onRefresh, filterList }} />
</div>
)
}
function Chart({ filterList, onRefresh }) {
console.log("Chart重新渲染了")
return (
<div>
<Select value={filterList} onChange={onRefresh}>
<Option value="option1">Option 1</Option>
<Option value="option2">Option 2</Option>
<Option value="option3">Option 3</Option>
</Select>
<MemoChild />
</div>
)
}
function ChildComponent() {
console.log("ChildComponent重新渲染了")
return <div>hello</div>
}
const MemoChild = React.memo(ChildComponent)
export default Adapt
子组件仍然可以进行正常的缓存,可得知当子组件使用memo
无论是父组件的state或者props状态变更,只要子组件的props和上下文未变化都不会影响到子组件
导致组件重复渲染的可能如下:
因此为了优化子组件的多次渲染应当注意: