父组件引用子组件例子
import {useState} from 'react';
const MemoTest = () => {
const [name, setName] = useState('名称')
const [content,setContent] = useState('内容')
return (
<>
<h3>{content}</h3>
<button onClick={() => setName(new Date().getTime())}>name</button>
<button onClick={() => setContent(new Date().getTime())}>content</button>
<Button name={name}></Button>
</>
)
}
export default MemoTest;
const Button = ({ name, children }) => {
// console.log('3333')
function changeName(name) {
console.log('changeName')
return name + '改变name的fun'
}
const otherName = changeName(name)
return (
<>
<div>{otherName}</div>
</>
)
}
运行时发现改变content的时候,也会触发子组件的changeName事件打印’changeName’,这不应该发生的!!!
用memo优化后的例子
import {useState, memo} from 'react';
const Button = ({ name, children }) => {
function changeName(name) {
console.log('changeName')
return name + '改变name的fun'
}
const otherName = changeName(name)
return (
<>
<div>{otherName}</div>
</>
)
}
const MemoChild = memo(Button)
const MemoTest = () => {
const [name, setName] = useState('名称')
const [content,setContent] = useState('内容')
return (
<>
<h3>{content}</h3>
<button onClick={() => setName(new Date().getTime())}>name</button>
<button onClick={() => setContent(new Date().getTime())}>content</button>
<MemoChild name={name} />
</>
)
}
export default MemoTest;
这样一直点击content都不会触发changeName了,这时候父组件发生状态的改变,子组件不会发生对应的重新执行
改成父组件传方法进子组件内调用
import {useState, memo, useCallback, useMemo} from 'react';
const Button = ({ name, onChangeName }) => {
console.log('3333')
function changeName(name) {
console.log('changeName')
onChangeName(name)
}
return (
<>
<div>{name}</div>
<button onClick={() => changeName(new Date().getTime())}>name</button>
</>
)
}
const MemoChild = memo(Button)
const MemoTest = () => {
const [name, setName] = useState('名称')
const [content,setContent] = useState('内容')
return (
<>
<h3>{content}</h3>
<button onClick={() => setContent(new Date().getTime())}>content</button>
<MemoChild name={name} onChangeName={newName => setName(newName)} />
</>
)
}
export default MemoTest;
点击content按钮看看效果
我只传了setName方法进子组件,触发的也是父组件的方法,为啥子组件也会触发呢?这是有问题的,我们用useCallback来解决
添加useCallback
把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
就这里加useCallback
<MemoChild name={name} onChangeName={useCallback(newName => setName(newName), [])} />
然后再点击content按钮看看,只有点击name按钮才会触发子组件
需要使用useMemo的例子
把传给子组件的name改成对象,添加一个age
import {useState, memo, useCallback, useMemo} from 'react';
const Button = ({ name, onChangeName }) => {
console.log('3333')
function changeName(name) {
console.log('changeName')
onChangeName(name)
}
return (
<>
<div>{name.name}</div>
<div>{name.age}</div>
<button onClick={() => changeName(new Date().getTime())}>name</button>
</>
)
}
const MemoChild = memo(Button)
const MemoTest = () => {
const [name, setName] = useState('名称')
const [content,setContent] = useState('内容')
return (
<>
<h3>{content}</h3>
<button onClick={() => setContent(new Date().getTime())}>content</button>
<MemoChild name={{name, age:18}} onChangeName={useCallback(newName => setName(newName), [])} />
</>
)
}
export default MemoTest;
然后疯狂点击content按钮,你会发现子组件怎么又被触发了,这个时候就需要useMemo来帮助了
添加useMemo
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
只要给name传递的对象数据添加useMemo即可
<MemoChild name={useMemo( () => ({name, age:18}), [name])} onChangeName={useCallback(newName => setName(newName), [])} />
然后再点击一下name和content按钮,就会发现只有name按钮会触发子组件,content按钮并不会触发
总结
- 在子组件不需要父组件的值和函数的情况下,也就是没有依赖关系的时候,用于减少子组件的重新渲染,当父组件传入的props改变时,子组件才会渲染,使用memo函数包裹子组件即可。
- 而在使用值和函数的情况,有函数传给子组件调用的时候要使用useCallback。
- 数据的优化的时候使用useMemo。