前言

:::warning react数组件不想类组件那样有生命周期函数,以及state。但是我们可以通过hook来优化我们的性能。
一个组件重新重新渲染,一般三种情况:

  1. 要么是组件自己的状态改变
  2. 要么是父组件重新渲染,导致子组件重新渲染,但是父组件的 props 没有改变
  3. 要么是父组件重新渲染,导致子组件重新渲染,但是父组件传递的 props 改变 :::

    React.memo

    1. function Test() {
    2. const [value,setValue] = useState(0)
    3. return ( <div>
    4. {value}
    5. <button onClick={()=>{setValue(1)}}>changeValue</button>
    6. <Testson name='lisi' />
    7. </div>
    8. )
    9. }
    10. function Testson(props) {
    11. console.log('Testson 重新渲染了')
    12. return (
    13. <p>{props.name}</p>
    14. )
    15. }

    在上面的代码执行的时候,Testson的props的值没有改变,只有父组件的value的值改变了。重新渲染了父组件,也重新渲染了子组件。
    image.png
    通过react.memo包裹子组件,在props不变的情况下,Testson是不会二次渲染的。修改后的代码如下

    1. const Testson = React.memo(function(props) {
    2. console.log('Testson 重新渲染了')
    3. return (
    4. <p>{props.name}</p>
    5. )
    6. })

    react.memo高级用法

    1. const Testson = React.memo(function(props) {
    2. console.log('Testson 重新渲染了')
    3. return (
    4. <p>{props.name}</p>
    5. )
    6. },(preProps,nextProps) => {
    7. //判断逻辑
    8. return true //false、true ===》false渲染、true不渲染
    9. })

    用法和类组件的shouldComponentUpdate()差不多;

    useCallBack

    我们再添加一个需求,就是通过Testson子组件修改父组件的myKey。代码如下。

    1. function Test() {
    2. const [value,setValue] = useState(0)
    3. const [myKey,setMyKey] = useState('mykey')
    4. return ( <div>
    5. {myKey}---{value}
    6. <button onClick={()=>{setValue(1)}}>changeValue</button>
    7. <Testson name='lisi' click={() => {setMyKey('myKey has changed')}}/>
    8. </div>
    9. )
    10. }
    11. const Testson = React.memo(function(props) {
    12. console.log('Testson 重新渲染了')
    13. return (
    14. <div>
    15. <button onClick={props.click}>changeMyKey</button>
    16. <p>{props.name}</p>
    17. </div>
    18. )

    再以上的代码执行中,点击changeValue按钮还是会重新渲染子组件,虽然props并没有改变,这是一个不必要的渲染。为什么会重新渲染来呢?这是因为父组件重新渲染了,所以props的值改变了,这个值是props.click,因为这是一个函数,每次渲染都会重新创建,所以它的指向也就不同了。就像创建了两个对象一样。我们通过usecCallback来处理;

    1. function Test() {
    2. const [value,setValue] = useState(0)
    3. const [myKey,setMyKey] = useState('mykey')
    4. const changeMykey = useCallback(() => {setMyKey('myKey has changed')},[])
    5. return ( <div>
    6. {myKey}---{value}
    7. <button onClick={()=>{setValue(1)}}>changeValue</button>
    8. <Testson name='lisi' click={changeMykey}/>
    9. </div>
    10. )
    11. }
    12. const Testson = React.memo(function(props) {
    13. console.log('Testson 重新渲染了')
    14. return (
    15. <div>
    16. <button onClick={props.click}>changeMyKey</button>
    17. <p>{props.name}</p>
    18. </div>
    19. )
    20. })

    通过以上代码就能解决了子组件不必要的渲染。

    useEffect

    在调用 useEffect 后,相当于告诉 React 在每一次组件更新完成渲染后,都调用传入 useEffect 中的函数,包括初次渲染以及后续的每一次更新渲染。 :::warning

  4. useEffect(effectCallback: () => void, deps: any[]) 接收两个参数,第二个参数依赖项是可选的,表示这个 effect 要依赖哪些值。

  5. 有时候我们并不想每次渲染 effect 都执行,只有某些值发生变化才去执行 effect,这个时候我们可以指定这个 effect 的依赖列表,可以是一个也可以几个,当其中列表中的某一个值发生变化,effect 才会执行。
  6. 第一个参数的返回值,会在组件卸载时执行,相当于 componentWillUnmount,可以清理定时器,移除事件监听,取消一些订阅。
  7. 当第二个参数为一个空数组如果第二个没传参数则每次组件更新完成渲染后都调用useEffect里面的回调函数时,相当于 componentDidMount 和 componentWillUnmount,表明这个 effect 没有任何依赖,只在首次渲染时执行。 :::

    useMemo

    useMemo可以缓存计算。学过vue的同学都知道vue有computed可以缓存计算的值。react和computed差不多。设计出来都是为了缓存计算;

    1. function App() {
    2. const [num, setNum] = useState(0);
    3. function expensiveFn() {
    4. let result = 0;
    5. for (let i = 0; i < 10000; i++) {
    6. result += i;
    7. }
    8. console.log(result)
    9. return result;
    10. }
    11. const base = useMemo(expensiveFn, []);
    12. return (
    13. <div className="App">
    14. <h1>count:{num}</h1>
    15. <button onClick={() => setNum(num + base)}>+1</button>
    16. </div>
    17. );
    18. }

    以上代码执行。不管点多少次+1都只会输出一次result,也就是expensiveFn值计算一次。通过useMemo缓存了expensiveFn的计算结果。