函数组件优化 - React.memo
React.memo
React.memo(ReactNode, [(prevProps, nextProps) => {}])
- 第一个参数:组件
- 第二个参数【可选】:自定义比拟函数。两次的 props 雷同的时候返回 true,不同则返回 false。返回 true 会阻止更新,而返回 false 则从新渲染。
如果把组件包装在 React.memo
中调用,那么组件在雷同 props 的状况下渲染雷同的后果,以此通过记忆组件渲染后果的形式来进步组件的性能体现。
React.memo
仅查看 props 变更,默认状况下其只会对简单对象做浅层比照,如果想要管制比照过程,那么请将自定义的比拟函数通过第二个参数传入来实现。
const MyComponent = React.memo(function MyComponent(props) { /* 应用 props 渲染 */}, (prevProps, nextProps) => { /* 如果把 nextProps 传入 render 办法的返回后果与 将 prevProps 传入 render 办法的返回后果统一则返回 true, 否则返回 false */})
论断
当父组件从新渲染时:
- 当子组件的 props 是根底类型,子组件不会反复渲染。不应该应用 React.memo。
当子组件的 props 是援用类型
- 子组件未应用 React.memo。子组件会反复渲染。
- 子组件应用 React.memo,并且不传入第二个参数。如果 props 未应用对应的 hook,那么会反复渲染,并且子组件多一次 diff 计算。如果援用类型应用对应的 hook,不会反复渲染。
- 子组件应用 React.memo,并且传入第二个参数。子组件是否反复渲染依据自定义函数决定。
测试
背景介绍
在一个父组件里有两个子组件,当父组件产生从新渲染时,两个子组件在不同的条件管制下是否会从新渲染?
字段解释
字段名 | 含意 | 测试意义 |
---|---|---|
title | 根底类型常量 | 测试根底类型扭转对子组件的影响 |
commonObject | 援用类型常量 | 测试援用类型扭转对子组件的影响;测试 hook(useMemo) 定义的援用类型扭转对子组件的影响 |
dataSource | useState 定义的援用类型 | 测试 hook(useState) 定义的援用类型扭转对子组件的影响 |
updateXxxxInfo | 办法 | 测试援用类型扭转对子组件的影响;测试 hook(useCallBack) 定义的援用类型扭转对子组件的影响 |
根底代码
子组件 BaseInfo:
const BaseInfo = (props) => { console.log('BaseInfo 从新渲染, props:', props) const { title, dataSource = {} } = props return ( <Card title={title}> <div>姓名:{dataSource.name}</div> </Card> )}
子组件 OtherInfo:
const OtherInfo = (props) => { console.log('OtherInfo 从新渲染, props:', props) const { title, dataSource } = props return ( <Card title={title}> <div>学校:{dataSource.school}</div> </Card> )}
父组件 FunctionTest:
function FunctionTest() { const [baseInfo, setBaseInfo] = useState({ name: '混沌' }) const [otherInfo, setOtherInfo] = useState({ school: '上海大学' }) return ( <Space direction="vertical" style={{ width: '100%' }}> <Space> <Button onClick={() => { console.log('点击-批改根本信息') setBaseInfo({ name: '貔貅' }) }} >批改根本信息</Button> <Button onClick={() => { console.log('点击-批改其余信息') setOtherInfo({ school: '北京大学' }) }} >批改其余信息</Button> </Space> <BaseInfo title="根本信息 - 子组件" dataSource={baseInfo} /> <OtherInfo title="其余信息 - 子组件" dataSource={otherInfo} /> </Space> )}
测试一:批改子组件 BaseInfo
为 React.memo 包裹
const BaseInfo = React.memo((props) => { console.log('BaseInfo 从新渲染, props:', props) const { title, dataSource = {} } = props return ( <Card title={title}> <div>姓名:{dataSource.name}</div> </Card> )})
点击“批改根本信息”后,BaseInfo 与 OtherInfo 全副从新渲染。
点击“批改其余信息”后,OtherInfo 从新渲染,BaseInfo 没有从新渲染。
论断:
- 当 props 是根本类型或 react hook(useState) 定义的援用类型时,应用 React.memo 能够阻止反复渲染。
- 应用了 React.memo 的 BaseInfo,当在 props 雷同时没有反复渲染。
测试二:在测试一的根底上,在父组件 FunctionTest
中增加援用类型,并传给两个子组件
测试2.1: 当援用类型的常量是一个对象/数组时
function FunctionTest() { //... const commonObject = {} //... return ( // ... <BaseInfo title="根本信息 - 子组件" dataSource={baseInfo} commonObject={commonObject} /> <OtherInfo title="其余信息 - 子组件" dataSource={otherInfo} commonObject={commonObject} /> // ... )}
点击“批改根本信息”或“批改其余信息”,BaseInfo 与 OtherInfo 全副从新渲染。
测试2.2: 当援用类型的常量是一个办法时:
function FunctionTest() { //... const updateBaseInfo = () => { console.log('更新根本信息,原数据:', baseInfo) setBaseInfo({ name: '饕餮' }) } const updateOtherInfo = () => { console.log('更新其余信息,原数据:', otherInfo) setOtherInfo({ school: '河南大学' }) } //... return ( //... <BaseInfo title="根本信息 - 子组件" dataSource={baseInfo} updateBaseInfo={updateBaseInfo} /> <OtherInfo title="其余信息 - 子组件" dataSource={otherInfo} updateOtherInfo={updateOtherInfo} /> //... )}
点击“批改根本信息”或“批改其余信息”,BaseInfo 与 OtherInfo 全副从新渲染。
论断:
- 当 props 蕴含援用类型时,应用 React.memo 并且不自定义比拟函数时不能阻止反复渲染。
- 无论有没有应用 React.memo 都会从新渲染,此时 BaseInfo 性能不如 OtherInfo, 因为 BaseInfo 多了一次 diff。
测试三:在测试二的根底上,增加 hook
测试3.1:给 commonObject 增加 useMemo hook
const commonObject = useMemo(() => {}, [])
点击“批改根本信息”后,BaseInfo 与 OtherInfo 全副从新渲染。
点击“批改其余信息”后,OtherInfo 从新渲染,BaseInfo 没有从新渲染。
测试3.2: 给 updateBaseInfo
与 updateOtherInfo
增加 useCallback hook
const updateBaseInfo = useCallback(() => { console.log('更新根本信息,原数据:', baseInfo) setBaseInfo({ name: '饕餮' })}, [])const updateOtherInfo = useCallback(() => { console.log('更新其余信息,原数据:', otherInfo) setOtherInfo({ school: '河南大学' })}, [])
点击“批改根本信息”后,BaseInfo 与 OtherInfo 全副从新渲染。
点击“批改其余信息”后,OtherInfo 从新渲染,BaseInfo 没有从新渲染。
论断:
- 当 props 的函数应用 useMemo/useCallback 时,应用 React.memo 并且不自定义比拟函数时能够阻止反复渲染。
- 应用了 React.memo 的 BaseInfo,当在 props 雷同时没有反复渲染。
测试四:在测试三的根底上,给 OtherInfo 增加 React.memo 并且自定义比拟函数
const OtherInfo = React.memo((props) => { console.log('OtherInfo 从新渲染, props:', props) const { title, dataSource, updateOtherInfo } = props return ( <Card title={title}> <div>学校:{dataSource.school}</div> <Button onClick={updateOtherInfo}>更新学校</Button> </Card> )}, (prevProps, nextProps) => { console.log('OtherInfo props 比拟') console.log('OtherInfo 老的props:', prevProps) console.log('OtherInfo 新的props:', nextProps) let flag = true Object.keys(nextProps).forEach(key => { let result = nextProps[key] === prevProps[key] console.log(`比拟 ${key}, 后果是:${result}`) if (!result) { flag = result } }) console.log(`OtherInfo 组件${flag ? '不会' : '会'}渲染`) return flag})
点击“批改根本信息”后,BaseInfo 从新渲染, OtherInfo 没有从新渲染。
点击“批改其余信息”后,BaseInfo 没有从新渲染,OtherInfo 从新渲染。
论断:
- 当 props 的函数应用 useMemo/useCallback 时,应用 React.memo 并且不自定义比拟函数时能够阻止反复渲染。
- React.memo 的第二个参数能够判断是否须要自定义渲染。