react 项目中,很常见的一个场景:
const [watchValue, setWatchValue] = useState('');
const [otherValue1, setOtherValue1] = useState('');
const [otherValue2, setOtherValue2] = useState('');
useEffect(() => {
doSomething(otherValue1, otherValue2);
}, [watchValue, otherValue1, otherValue2]);
我们想要watchValue 改变的时候执行doSomething ,里面会引用其他值otherValue1 , otherValue2 。
这时有个让人烦恼的问题:
- 如果不把
otherValue1 , otherValue2 加入依赖数组的话,doSomething 里面很可能会访问到otherValue1 , otherValue2 旧的变量引用,从而发生意想不到的错误(如果安装hooks 相关eslint 的话,会提示警告)。
- 反之,如果把
otherValue1 , otherValue2 加入依赖数组的话,这两个值改变的时候doSomething 也会执行,这并不是我们想要的(我们只想引用他们的值,但不想它们触发doSomething )。
把otherValue1 , otherValue2 变成ref 可以解决这个问题:
const [watchValue, setWatchValue] = useState('');
const other1 = useRef('');
const other2 = useRef('');
// ref可以不加入依赖数组,因为引用不变
useEffect(() => {
doSomething(other1.current, other2.current);
}, [watchValue]);
这样other1 , other2 的变量引用不会变,解决了前面的问题,但是又引入了一个新的问题:other1 , other2 的值current 改变的时候,不会触发组件重新渲染(useRef的current 值改变不会触发组件渲染),从而值改变时候界面不会更新!
这就是hooks 里面的一个头疼的地方,useState 变量会触发重新渲染、保持界面更新,但作为useEffect 的依赖时,又总会触发不想要的函数执行。useRef 变量可以放心作为useEffect 依赖,但是又不会触发组件渲染,界面不更新。
如何解决?
可以将useRef 和useState 的特性结合起来,构造一个新的hooks 函数: useStateRef 。
import { useState, useRef } from "react";
// 使用 useRef 的引用特质, 同时保持 useState 的响应性
type StateRefObj<T> = {
_state: T;
value: T;
};
export default function useStateRef<T>(
initialState: T | (() => T)
): StateRefObj<T> {
// 初始化值
const [init] = useState(() => {
if (typeof initialState === "function") {
return (initialState as () => T)();
}
return initialState;
});
// 设置一个 state,用来触发组件渲染
const [, setState] = useState(init);
// 读取value时,取到的是最新的值
// 设置value时,会触发setState,组件渲染
const [ref] = useState<StateRefObj<T>>(() => {
return {
_state: init,
set value(v: T) {
this._state = v;
setState(v);
},
get value() {
return this._state;
},
};
});
// 返回的是一个引用变量,整个组件生命周期之间不变
return ref;
}
这样,我们就能这样用:
const watch = useStateRef('');
const other1 = useStateRef('');
const other2 = useStateRef('');
// 这样改变值:watch.value = "new";
useEffect(() => {
doSomething(other1.value, other2.value);
// 其实现在这三个值都是引用变量,整个组件生命周期之间不变,完全可以不用加入依赖数组
// 但是react hooks的eslint插件只能识别useRef作为引用,不加人会警告,为了变量引用安全还是加入
}, [watch.value, other1, other2]);
这样,watch, other1 ,other2 有useRef 的引用特性,不会触发doSomething 不必要的执行。又有了useState 的响应特性,改变.value 的时候会触发组件渲染和界面更新。
我们想要变量改变触发doSomething 的时候,就把watch.value 加入依赖数组。我们只想引用值而不想其触发doSomething 的时候,就把变量本身加入数组。
以上就是React 小技巧教你如何摆脱hooks依赖烦恼的详细内容,更多关于React hooks依赖的资料请关注社区其它相关文章! |