持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
无优化:
function Child(props){
console.log("child render");
return(
<div>
<p>child {props.data.count}</p>
<button onClick={props.handleCount}>child click</button>
</div>
)
}
上面Child组件中分别引用了父组件中data对象中的count变量,和改变count的handleCount函数方法
function App(){
console.log("render father");
const [count,setCount] = useState(0)
const [value,setValue] = useState('')
let data = {count:count}
let handleCount = ()=>{
setCount(count+1)
}
let handleValue=(e)=>{
setValue(e.target.value)
}
return(
<div>
<p>father {count}</p>
<p>father {value}</p>
<input type="text" value={value} onChange={handleValue}/>
<hr />
<MemoChild data={data} handleCount={handleCount}/>
</div>
)
}
运行该代码可知:data中的count引用父组件中count变量,当点击子组件中按钮时,setCount会+1,结果导致父组件重新render,子组件也重新render。输入框输入时,也导致父子组件都重新声明。
缓存数据:useMemo
情况1. data中count数据如果是常数0,当点击按钮改变父组件中count后,即使子组件中数据仍然是0,也会使父组件中count+1的同时,使let重新声明data,产生新地址,重新渲染child子组件。
let data = {count:0}
(按钮是修改父组件中count的)
优化:
将钩子函数useMemo包裹住:
let data = useMemo(()=>{
return {count:0}
},[]) //注意,为[],只声明一次,每次获得的都是0
子组件始终为常数0,可以用[]始终data被缓存一次,count为0就成为始终没变的,那父组件改变无关子组件时,只挂载一次值,不会再刷新。
情况2. data中count数据如果是count变量的话。父组件改变的情况:
let data = {count:count}
当count改变时,子组件会重新声明,有两种渲染情况。
let data = useMemo(()=>{
return {count:count}
},[]) //只执行一次,页面显示的还只是最开始的缓存值
let data = useMemo(()=>{
return {count:count}
},[count]) //依赖于count,当count改变就会每次动态的返回具体值
函数缓存:useCallback
子组件中引用父组件中声明的函数,当没有调用此方法修改数据时,该方法也会被重新渲染产生新地址
function Child(props){
console.log("child render");
return(
<div>
<p>child {props.data.count}</p>
<button onClick={props.handleCount}>child click</button>
</div>
)
}
function App(){
console.log(" father");
const [count,setCount] = useState(0)
const [value,setValue] = useState('')
//data对象已经完成缓存
let data = useMemo(()=>{return {count:count}},[count])
let handleCount = ()=>{setCount(count+1)}
let handleValue=(e)=>{setValue(e.target.value)}
return(
<div>
<p>father {count}</p>
<p>father {value}</p>
<input type="text" value={value} onChange={handleValue}/>
<hr />
<MemoChild data={data} handleCount={handleCount}/>
</div>
)
}
优化方法:useCallback 将handleCount传进子组件前进行函数缓存。
情况1:[ ]约束
let handleCount = useCallback(()=>{
console.log(count)
setCount(count+1)
},[])
[ ]使得函数只执行一次,只获取一次值,所以每次点击完按钮打印count时,获得的都是0,因为它只执行了一次,函数内setCount只进行一次加一的操作。但是虽然成功完成加一,但也不会渲染到子组件页面上。
情况2:[count]
let handleCount = useCallback(()=>{
console.log(count)
setCount(count+1)
},[count])
每次依赖于count数据,每次改变都会渲染,与count无关的数据改变就不会使子组件重新render。
比如改变count时,子组件会重新渲染,但输入文字时,子组件不会重新声明。
熟练使用:
熟练使用useCallback缓存函数,使useMemo缓存对象,使memo缓存函数组件,使pureComponent缓存类组件,使用shouldComponentUpedate自定义是否更新函数。