useCallback的使用方式在这里就不重复描述了,
(注:可以看这篇文章:https://blog.youkuaiyun.com/rock_23/article/details/121410694)
useMemo的使用
import { useState, useMemo } from 'react'
import { Button } from 'antd'
const Home: React.FC = () => {
const [ count, setCount ] = useState<number>(0)
// 因为没有使用useMemo的原因,导致点击多少次按钮,就会调用多少次
const countToString = (() => {
console.log('countToString: 被调用')
return count.toString()
})()
const memoCountToString = useMemo(() => {
console.log('memoCountToString: 被调用')
return count.toString()
// useMemo如果不设置第二参数为count的话,那么这个函数无论点击
// 多少次,都只会打印一次,而设置了第二参数要监听的数据后,
// 当你内容改变了,才会触发
}, [count])
return <>
<div>
<div>------------ 分界线 useMemo -------------</div>
<h3>countToString: {countToString}</h3>
<h3>memoCountToString:{memoCountToString}</h3>
<Button onClick={() => setCount((code) => code + 1)}>usememo count</Button>
<Button onClick={() => setCount(count + 1)}>useMemo +1</Button>
</div>
</>
}
export default Home
useCallback和useMemo一起使用
解决:
为了使 Button 组件不进行重复渲染,我们必须这么做
-
必须使用 useCallback hook来包裹 onClick 函数。
-
并且使用 useMemo hook 包裹Button 组件
//Index.js
import React, { useState, useCallback } from 'react';
//引入按钮组件
import {Button} from 'antd';
const Index = () => {
const [count, setCount] = useState(0);
//使用useCallback 方法包裹 第二个参数我们给空数组 让这个函数永远不会更新
//注意这个函数永远不会更新的话 setCount 就必须写成funtion 不然拿不到想要的count状态
const onClick = useCallback(()=> {
setCount(prevState => prevState + 1)
}, [])
console.log('Index组件渲染')
return <>
<p>count:{count}</p>
{/* 使Button组件也永远不要更新 */}
{ useMemo(()=><Button onClick={onClick}>加1</Button> , []}) }
</>
}
export default Index;
知识延伸:
了解useCallback的使用即可,在项目中考虑到了优化问题,所以将useMemoizedFn来替换useCallback,原因:
很多时候useCallback函数组件props的一部分,因为每次渲染的时候都会重新创建callback导致函数的引用不同(引用地址,看不懂的可以去了解一下js原理,就能稍稍理解些意思了),所以触发了组件的重渲染。然后一旦函数使用useCallback包裹,则要面对声明式依赖项的问题,对于一个内部捕获了很多的state的函数,写依赖项非常容易写错,因此引发bug。所以,多数场景下只需要维持函数引用的情况下使用useCallback。否则,建议使用useMemoizedFn来进行替代useCallback,这样既能保持引用,又不用声明依赖项。
既能保持函数引用地址不变,又不用声明依赖项,而内部变量还可以捕获最新的state(useMemoizedFn,useRef,useMemo)
useMemo
useMemo需要声明依赖项,而memo不需要
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算
单独使用 memo 也可以搞定 useCallback + useMemo组合。
但是需要注意:当 memo 第二个参数返回 true 时候组件将不会被更新,也就是上一次的 props 的更新将不会用到组件中,可能会造成一些奇怪的问题。所以如果你控制不住的话,最好不要使用。
//Button.js 组件
const Button = (props) => {
console.log('Botton组件渲染 --memo包裹')
return <button onClick={props.onClick}>
{props.children}
</button>
}
export default memo(Button, (prevProps, nextProps)=>{
//可以使用prevProps, nextProps进行一些逻辑判断
return true
})