@wjt blog @wjt blog
首页
  • react
  • react-native
  • webpack
  • 其他
  • docker
  • liunx命令
读书
收藏

@wjt

遇见知识
首页
  • react
  • react-native
  • webpack
  • 其他
  • docker
  • liunx命令
读书
收藏
  • react

    • react渲染流程
    • react自定义hook
      • react性能优化之自定义hooks
        • 场景1. 当通过useState设置useEffect监听的对象时。不管对象的属性值变没变都会执行useEffect回调函数.
        • 场景2. 当useEffect依赖多个值时,每个值的改变都会触发回调函数的执行。
  • react-native

  • webpack

  • 其他

  • 前端
  • react
@wjt
2023-07-06
目录

react自定义hook

# react性能优化之自定义hooks

# 场景1. 当通过useState设置useEffect监听的对象时。不管对象的属性值变没变都会执行useEffect回调函数.

  1. 前置条件
  • useState设置对象往往需要返回新的引用
import React, { useState, useEffect, useRef } from "react";

export default function App() {
  const paramsRef = useRef(null)
  const [params, setParams] = useState({
    a: 1,
    b: 2
  });

  const changeParams = () => {
    setParams(old => {
      return {
        ...old,
        a: 1
      }
    })
  }

  useEffect(() => {
    console.log(params === paramsRef.current);
    // 每次触发changeParams都会打印false
    paramsRef.current = params
  }, [params])
  return (
    <>
      <p>{JSON.stringify(params)}</p>
      <button onClick={changeParams}>改变</button>
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  • useEffect针对对象是浅比较(引用相等,在js中===是做浅比较,只检查左右两边是否是同一个对象的引用)的
  • 注意-因为useEffect的浅比较所以直接修改对象的属性值不会触发回调
setParams(old => {
    // old 还是原来的引用,不会触发useEffect的回调
    old.a = 4;
    return old
})

const [params, setParams] = useState({
    a: 1,
    b: 2
});
...
params.a = 4;
// params 还是原来的引用,不会触发useEffect的回调.
// 当然应该没人会这么写!!!
setParams(params)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  1. 期望-只有对象的值改变之后触发回调

  2. 实现期望-自定义hook(useDeepEffect)

  • 1.使用useRef保存需要深度监听的变量
  • 2.使用useRef声明一个计数器
  • 3.深度对比变量和useRef的值。不相等计数器++
  • 4.返回一个监听计数器的useEffect
import { useRef, useEffect } from 'react';
import { isEqual } from 'lodash';
export const useDeepEffect = (cb, dep) => {
    const numRef = useRef(0);
    const depRef = useRef(dep)
    // useRef可以在组件内一直保持一个引用
    if (!isEqual(dep, depRef.current)) {
        numRef.current ++;
    }
    depRef.current = dep;
    return useEffect(cb, [numRef.current])
}
1
2
3
4
5
6
7
8
9
10
11
12
  1. 使用
useDeepEffect(() => {
    // 当params对象的属性值发生变化的时候触发
}, params)
1
2
3

# 场景2. 当useEffect依赖多个值时,每个值的改变都会触发回调函数的执行。

  1. 期望-一些回调函数执行的方法依赖于多个监听的值。这样这不仅会出现多次执行的情况。还会导致异步数据处理的时候出现不符合预期的情况。所以希望实现只有在最后一个值变化完成后执行的回调hook
import React, { useState, useEffect } from "react";
// import { useFinalEffect } from './customHooks'
export default function App() {
  const [o1, setO1] = useState({a: 1});
  const [o2, setO2] = useState({b: 1});
  const [o3, setO3] = useState({c: 1});
  useEffect(() => {
    console.log('我都执行了'); // 执行了4遍
  }, [o1, o2, o3])
  useEffect(() => {
    setTimeout(() => {
      setO1({a: 2})
    }, 1000)
    setTimeout(() => {
      setO2({b: 2})
    }, 1000)
    setTimeout(() => {
      setO3({c: 2})
    }, 1000)
  }, [])
  return (
    <div>
      
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  1. 实现期望-自定义hook(useFinalEffect)
  • 1.使用useRef保存需要深度监听的依赖数组(depRef)
  • 2.使用useRef声明一个计数器(numRef)
  • 3.对比当前依赖和depRef每一项的值,有变化的numRef加一
  • 4.返回一个监听计数器的useEffect。当numRef计数器等于depRef长度的时候触发回调
export const useFinalEffect = (cb, dep) => {
    const numRef = useRef(0);
    const depRef = useRef(dep)
    if (Array.isArray(dep) && dep.length > 1) {
        dep.forEach((item, index) => {
            if (!isEqual(depRef.current[index], item)) {
                numRef.current ++;
            }
        })
    } else {
        console.log('请依赖至少两个对象');
    }
    
    depRef.current = dep;
    return useEffect(() => {
        if (numRef.current === dep.length) {
            cb()
        }
    }, [numRef.current])
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  1. 使用
  • 使用场景
  • 4.1.依赖项长度大于1
  • 4.2.依赖的每一项都会异步改变
useFinalEffect(() => {
    console.log('我都执行了'); // 在最后一个值变更的时候更新
}, [o1, o2, o3])
1
2
3
react渲染流程
macOS Android环境搭建

← react渲染流程 macOS Android环境搭建→

最近更新
01
find
07-06
02
cp
07-06
03
vim
07-06
更多文章>
Theme by Vdoing | Copyright © 2023-2023 @wjt | 浙ICP备2023018372号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式