react自定义hook
# react性能优化之自定义hooks
# 场景1. 当通过useState设置useEffect监听的对象时。不管对象的属性值变没变都会执行useEffect回调函数.
- 前置条件
- 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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
期望-只有对象的值改变之后触发回调
实现期望-自定义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
2
3
4
5
6
7
8
9
10
11
12
- 使用
useDeepEffect(() => {
// 当params对象的属性值发生变化的时候触发
}, params)
1
2
3
2
3
# 场景2. 当useEffect依赖多个值时,每个值的改变都会触发回调函数的执行。
- 期望-一些回调函数执行的方法依赖于多个监听的值。这样这不仅会出现多次执行的情况。还会导致异步数据处理的时候出现不符合预期的情况。所以希望实现只有在最后一个值变化完成后执行的回调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
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
- 实现期望-自定义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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 使用
- 使用场景
- 4.1.依赖项长度大于1
- 4.2.依赖的每一项都会异步改变
useFinalEffect(() => {
console.log('我都执行了'); // 在最后一个值变更的时候更新
}, [o1, o2, o3])
1
2
3
2
3