在使用useEffect、useCallback这些hooks,为什么还要写依赖数组呢,不写这些依赖,React还给你警告。
下面我就来分享一下自己的理解
声明依赖的目的:防止函数的闭包特性产生意外的错误
前言
在b站看到一个up用JS实现红绿灯,我就试着自己也写一个。
目的是想要它每个1秒跳转到下一个灯。
红 => 绿 => 黄 => 红 => 绿 => 黄
这样的顺序
代码
但是我发现,下面这串代码只有绿灯和黄灯亮。
import React, { useState, useEffect } from "react";
import classes from "./TrafficLight.module.css";
import Light from "./Light";
let curIdex = 0;
const TrafficLight = () => {
const [lights, setLights] = useState([
{ on: true, color: "red", id: 0 },
{ on: false, color: "green", id: 1 },
{ on: false, color: "yellow", id: 2 },
]);
console.log('current lights: ', lights);
useEffect(() => {
let curIdex = 0;
const timer = setInterval(() => {
curIdex += 1;
if (curIdex >= lights.length) curIdex = 0;
console.log('received lights', lights)
toggleLight(lights[curIdex]);
}, 1000 * 1);
}, []);
return () => clearTimeout(timer);
}, [lights]);
const toggleLight = (curLight) => {
setLights(
lights.map((light) =>
light === curLight
? { ...light, on: !light.on }
: { ...light, on: false }
)
);
};
const lightList = lights.map((light) => {
return ;
});
return {lightList};
};
export default TrafficLight;
//---------------分割线-----------------
import React from "react";
import classes from "./Light.module.css";
const Light = ({ light, onToggle }) => {
return (
onToggle(light)}
/>
);
};
export default Light;
打开控制面板看一下,发现每次调用 toggleLight 时,获取的 lights 还是停留在一开始的状态
,虽然 TrafficLight 组件的 lights 已经更新了,但是 toggleLight 函数获取到的 lights 并没有更新,所以才会导致每次红灯都不亮。
toggleLight 产生闭包的原因: useEffect 依赖数组为空,只会在首次渲染时调用它。
TrafficLight组件第一次渲染:
调用 useEffect 并声明了它的回调函数:
useEffect(() => {
let curIdex = 0;
const timer = setInterval(() => {
curIdex += 1;
if (curIdex >= lights.length) curIdex = 0;
toggleLight(lights[curIdex]);
}, 1000 * 1);
}, []);
此时这个回调函数获取到的 lights 是我们预设的 lights。
TrafficLight这个组件函数执行完毕后,函数产生的执行上下文也就从 stack 里移除了。
但是,setInterval 的回调函数又会在1秒之后执行,它更新了组件的 state,组件开始第二次渲染。
TrafficLight组件二次渲染:
lights 此时已经更新了,红灯的 on 变成了 false。但是useEffect不会被再次调用,因为它的依赖是空数组。这就导致useEffect的回调函数也不会被重新定义,setInteval还是会调用第一次渲染时定义的回调函数。因为函数的词法作用域,导致这个函数一直获取不到更新后的数据。
为了避免这种情况,我们就需要添加依赖数组。
总结 dependency,依赖,正如它名字所描述的一样,useEffect的回调函数的正常运行,有时会依赖于某些外部变量。如果这些变量发生了改变,回调函数获取到的变量却并不会同步更新。只重新定义这个回调函数,它才能获取到更新后的变量。重新定义它的回调函数,也就等于要重新调用useEffect。useCallback也是同样的道理
【理解为什么要给useEffect声明依赖】css代码
.container {
height: 20rem;
width: 7rem;
background-color: #2c3e50;
border-radius: 3.5rem;
padding: 0.7rem 0;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
position: fixed;
top: 1rem;
right: 2rem;
}
.light {
width: 4rem;
height: 4rem;
border-radius: 50%;
background-color: rgba(0, 0, 0, 0.35);
display: flex;
justify-content: flex-start;
align-items: center;
}.light::after {
content: "";
display: block;
width: 80%;
height: 80%;
border-radius: 50%;
border-right: 4px solid rgba(255, 255, 255, 0.6);
}.light.red {
background-color: #c0392b;
box-shadow: 0 0 20px 5px #c0392b;
}.light.yellow {
background-color: #f1c40f;
box-shadow: 0 0 20px 5px #f1c40f;
}.light.green {
background-color: #2ecc71;
box-shadow: 0 0 20px 5px #2ecc71;
}