[英]Prevent infinite loop in useEffect when setting state
I am currently building a scheduling app.我目前正在构建一个调度应用程序。 If a user selects two dates, I am attempting to select all date blocks between the two selected dates in the calendar as well.
如果用户选择两个日期,我也在尝试 select 日历中两个选定日期之间的所有日期块。 I am able to achieve this, but it causes my useEffect to fire into an infinite loop because I have state as a dependency in my useEffect where I am setting state.
我能够实现这一点,但它会导致我的 useEffect 进入无限循环,因为我在我设置 state 的 useEffect 中有 state 作为依赖项。 I am unsure of the best method to prevent the infinite loop behavior.
我不确定防止无限循环行为的最佳方法。 The useEffect in question is the bottom one.
有问题的 useEffect 是最底层的。 My code is as follows:
我的代码如下:
export default function App() {
const [selectedDate, handleDateChange] = useState(
dayjs().format("YYYY-MM-DD")
);
const [events] = useState([
{
id: "5e24d1fa-aa66-4122-b1eb-97792f0893b0",
name: "Rodriquez Family",
selectedDates: ["2021-05-01"],
status: "submitted"
},
{
id: "269a0381-63c7-4ab6-92d8-7f7b836aee6f",
name: "Test Family",
selectedDates: ["2021-05-03"],
status: "submitted"
}
]);
const [data, setData] = useState([]);
const getDaysArray = async (firstDay, lastDay) => {
let dates = [];
var dow = dayjs(firstDay).day();
while (dow > 0) {
dates.push(null);
dow = dow - 1;
}
while (firstDay <= lastDay) {
dates.push(firstDay);
firstDay = dayjs(firstDay).add(1, "days").format("YYYY-MM-DD");
}
return dates;
};
useEffect(() => {
const getDates = async () => {
const firstDay = dayjs(selectedDate)
.startOf("month")
.format("YYYY-MM-DD");
const lastDay = dayjs(firstDay).endOf("month").format("YYYY-MM-DD");
const dates = await getDaysArray(firstDay, lastDay);
const list = dates.map((date) => {
const event = events.find(({ selectedDates = [] }) =>
selectedDates.includes(date)
);
return event ? { date, event } : { date, event: null, checked: false };
});
setData(list);
};
getDates();
}, [events, selectedDate]);
const selectDate = (date) => {
setData(
(a) =>
a &&
a.map((item) =>
item.date === date ? { ...item, checked: !item.checked } : item
)
);
};
useEffect(() => {
if (data && data.filter((res) => res.checked).length > 1) {
const filterDates = data.filter((r) => r.checked);
const startDate = filterDates[0].date;
const endDate = filterDates[filterDates.length - 1].date;
const datesToUpdate = data.filter(
(res) => res.date > startDate && res.date < endDate
);
const newArr = data.map((date) => {
const updateCheck = datesToUpdate.find((r) => r.date === date.date);
return updateCheck ? { ...updateCheck, checked: true } : date;
});
setData(newArr);
}
}, [data]);
return (
<MuiPickersUtilsProvider utils={DayJsUtils}>
<div className="App">
<DatePicker
minDate={dayjs()}
variant="inline"
openTo="year"
views={["year", "month"]}
label="Year and Month"
helperText="Start from year selection"
value={selectedDate}
onChange={handleDateChange}
/>
</div>
<div className="cal">
<div className="cal-div1"></div>
<div className="cal-div2 "></div>
<div className="cal-div3 cal-cir-hov"></div>
<div className="cal-div4"> SUN </div>
<div className="cal-div5"> MON </div>
<div className="cal-div6"> TUE </div>
<div className="cal-div7"> WED </div>
<div className="cal-div8"> THU </div>
<div className="cal-div9"> FRI </div>
<div className="cal-div10"> SAT </div>
{data &&
data.map((r, i) => {
return (
<>
<div
onClick={() =>
!r.checked &&
r.date >= dayjs().format("YYYY-MM-DD") &&
!r.event &&
selectDate(r.date)
}
style={
r.checked
? { backgroundColor: "green" }
: { color: "#565254" }
}
key={i}
className="cal-cir-hov"
>
<div>{r.date} </div>
<div
style={
r.event?.status === "submitted"
? { color: "orange" }
: { color: "green" }
}
>
{r.event?.name}
</div>
</div>
</>
);
})}
</div>
</MuiPickersUtilsProvider>
);
}
attached is a code sandbox for debugging and to show the behavior I am currently talking about.附件是一个代码沙箱,用于调试并显示我目前正在谈论的行为。 Select two separate dates that are greater than today and you will see all the dates in between are selected, but the app goes into a loop https://codesandbox.io/s/dawn-snow-03r59?file=/src/App.js:301-4499
Select 两个大于今天的单独日期,您将看到中间的所有日期都被选中,但应用程序进入循环https://codesandbox.io/s/dawn-snow-03r59?file=/src/App .js:301-4499
If your useEffect depends on a variable that you're updating on the same useEffect there will always be the re-render and cause a loop.如果你的 useEffect 依赖于你在同一个 useEffect 上更新的变量,那么总是会重新渲染并导致循环。
If you want it to execute only once, you should remove the data
variable from the useEffect dependency array.如果你希望它只执行一次,你应该从 useEffect 依赖数组中移除
data
变量。
But if you really wanna mutate the state every time that the data
variable changes, my recommendation is to create another state for the mutated data.但是,如果您真的想在每次
data
变量更改时对 state 进行变异,我的建议是为变异数据创建另一个 state。
For example setFormattedData
would not change the data itself, but you would still have a state for this data in the format that you want.例如
setFormattedData
不会更改数据本身,但您仍然可以使用您想要的格式为该数据提供 state。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.