简体   繁体   English

设置 state 时防止 useEffect 中的无限循环

[英]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.

相关问题 useEffect - 更新状态时防止无限循环 - useEffect - Prevent infinite loop when updating state 更新 state 并包含依赖数组时使用 Effect 无限循环 - useEffect infinite loop when updating state and including dependency array 在 useEffect() 中改变状态时反应无限循环 - React infinite loop on change state in useEffect() 为什么 Redux state 中的 useEffect 导致无限循环: - Why useEffect in Redux state is causing infinite loop: 在useEffect中使用setState时如何防止我的React代码中的无限循环 - How to prevent infinite loop in my React code when using setState inside useEffect 在 useEffect 依赖数组中使用 Redux state 时如何避免无限循环? - How do I avoid infinite loop when using Redux state in useEffect dependency array? 使用带有所需函数的 useEffect 时的无限循环 - infinite loop when using useEffect with a required funcion 如何在 useEffect 中包含缺少的依赖项但防止 function 的无限循环执行? - How to include missing dependency in useEffect but prevent infinite loop execution of function? 使用默认参数和 useEffect 防止函数组件无限循环 - Prevent infinite loop on function component with default parameter and useEffect 如何更新 state 但不触发无限 useEffect 循环? - How do I update state but not trigger the infinite useEffect loop?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM