副标题#e#
useEffect中的第二个参数,可以是一个参数数组(依赖数组)。React更新DOM的思想,不管过程怎样,只将结果展示给世人。
React在更新组件的时候,会对比props,通过AST等方式比较,然后仅需更新变化了的DOM。
第二个参数相当于告诉了useEffect,只要我给你的这些参数任中之一发生了改变,你就执行effect就好了。如此,便可以减少每次render之后调用effect的情况,减少了无意义的性能浪费。
那么在开发过程中,我们会尝试在组件载入时候,通过api获取远程数据,并运用于组件的数据渲染,所以我们使用了如下的一个简单例子:
useEffect(() => {
featchData();
}, []);
由于是空数组,所以只有在组件挂载(mount)时获取一遍远程数据,之后将不再执行。如果effect中有涉及到局部变量,那么都会根据当前的状态发生改变,函数是每次都会创建(每次都是创建的新的匿名函数)。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
你可能会认为上面的例子,会在组件加载后,每秒UI上count+1,但实际情况是只会执行一次。为什么呐?是不是觉得有些违反直觉了?
因为,并没有给effect的依赖项加入count,effect只会在第一次渲染时候,创建了一个匿名函数,尽管通过了setInterval包裹,每秒去执行count + 1,但是count的值始终是为0,所以在UI表现上永远渲染的是1。
当然,通过一些规则,我们可以通过加上count来改变其值,或者通过useRef,或者通过setState(x => x+1),模式来实现获取最新的值。例如下面的黑科技操作:
// useRef
function Example() {
const [count, setCount] = useState(0);
const countRef = useRef(count);
countRef.current = count; // 假如这一行代码放到effect函数中会怎么样呐?可以思考下!
// answer: 在effect中count是effect匿名函数声明时就有了,值就是0,那么拿到的count值自然也是渲染前(本次props中的值)的count(值为0,再次复盘理解下快照的概念),但由于依赖数组中并不存在任何依赖,所以该匿名函数不会二次执行。
// 但,由于setInterval的原因,函数会不停地setCount,关键是其中的参数了,countRef.current = count;取到的值是第一次快照时候的值0,所以其更新的值永远为0+1 = 1。这样的结果是符合预期规则的。
// 那为什么放在外面就好了呐?因为countRef.current同步了count的最新值,每次render前就拿到了新的count值,并且赋值给countRef.current,由于ref的同步特性(及时性、统一性),所以循环中获取的countRef.current也是最新的值,故而能实现计数效果
useEffect(() => {
const id = setInterval(() => {
setCount(countRef.current + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
// setState传入函数
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
#p#副标题#e#
setCount(x => x + 1); // 传递参数为一个函数时候,默认传递的第一个参数是之前的值,这是useState的hook在处理
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
// 使用useReducer
function Counter({ step }) {
const [count, dispatch] = useReducer(reducer, 0);
function reducer(state, action) {
if (action.type === 'tick') {
return state + step;
} else {
throw new Error();
}
}
useEffect(() => {
const id = setInterval(() => {
dispatch({ type: 'tick' });
}, 1000);
return () => clearInterval(id);
}, [dispatch]);
return <h1>{count}</h1>;