react项目中,很常见的一个场景:
const [watchValue, setWatchValue] = useState('');
const [otherValue1, setOtherValue1] = useState('');
const [otherValue2, setOtherValue2] = useState('');
useEffect(() => {
doSomething(otherValue1, otherValue2);
}, [watchValue, otherValue1, otherValue2]);
复制代码
我们想要watchValue改变的时候执行useEffect,里面会引用其他值。
这时有个让人烦恼的问题:
- 如果不把
otherValue1, otherValue2加入依赖数组的话,useEffect里面很可能会引用到otherValue1, otherValue2旧的变量,从而发生意想不到的错误(如果安装hooks相关eslint的话,会提示警告)。 - 反之,如果把
otherValue1, otherValue2加入依赖数组的话,这两个值改变的时候useEffect里面的函数也会执行,这并不是我们想要的。
把otherValue1, otherValue2变成ref可以解决这个问题:
const [watchValue, setWatchValue] = useState('');
const other1 = useRef('');
const other2 = useRef('');
useEffect(() => {
doSomething(other1.current, other2.current);
}, [watchValue, other1, other2]);
复制代码
这样other1, other2不会变,解决了前面的问题,但是又引入了一个新的问题:other1, other2的值current改变的时候,不会触发组件重新渲染(useRef的current值改变不会触发组件渲染),从而值改变时候界面不会更新!
这就是hooks里面的一个头疼的地方,useState会触发重新渲染,但不好作为useEffect的依赖(会触发不需要的useEffect), useRef可以放心作为useEffect依赖,但是又不会触发组件渲染,界面不更新。
如何解决?
可以将useRef和useState的特性结合起来,构造一个新的hooks函数: useStateRef。
import { useState, useRef } from "react";
// 使用 useRef 的引用特质, 同时保持 useState 的响应性
type StateRefObj<T> = {
value: T;
};
export default function useStateRef<T>(
initialState: T | (() => T)
): StateRefObj<T> {
const [state, setState] = useState(() => {
if (typeof initialState === "function") {
return (initialState as () => T)();
}
return initialState;
});
const stateRef = useRef(state);
stateRef.current = state;
const [ref] = useState<StateRefObj<T>>(() => {
return {
set value(v: T) {
setState(v);
},
get value() {
return stateRef.current;
},
};
});
return ref;
}
复制代码
这样,我们就能这样用:
const watch = useStateRef('');
const other1 = useStateRef('');
const other2 = useStateRef('');
// 这样改变值:watch.value = "new";
useEffect(() => {
doSomething(other1.value, other2.value);
}, [watch.value, other1, other2]);
复制代码
这样,other1, other2既有useRef的引用特性,不会触发useEffect不必要的执行。而我么把watch.value(而不是watch)加入依赖数组,这样watch的值改变时也会执行useEffect,达到watch的效果。
同时,又保持了useState的响应特性,通过.value改变值得时候,同时也会触发组件渲染和界面更新。
注:这里之所以用
.value而不用.current, 是刻意为了保持和useRef的区别。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)
![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
