如何在添加依赖项时使用useEffect()钩子时避免无限循环?

时间:2020-06-26 01:18:42

标签: javascript reactjs ecmascript-6 react-hooks momentjs

我正在制作这样的TimePicker

enter image description here

,我还有一个状态是持续时间(从开始到结束的小时数),我使用useEffect()设定了开始或持续时间更改功能时的自动更改结束时间。

当我尝试更改持续时间时就可以了

但是更改开始时间或结束时间时出现错误 这是我的代码:

    useEffect(() => {
        setEnd(moment(start).add(duration, 'hours'));
    }, [start, duration]);
    useEffect(() => {
        setDuration(moment.duration(end.diff(start)).asHours());
    }, [end, start]);

CodeSanBox Sample(这个问题有点不同,因为我改进了有关矩的逻辑)

更新:感谢您的所有帮助,我做到了,这是最终代码: CodeSanBox Final

2 个答案:

答案 0 :(得分:2)

您的问题在于,您正在修改useEffect挂钩所依赖的东西。如果您的挂钩仅应在time更改时运行,则请勿更改挂钩中的time,否则它将无限循环

如果您确实需要反馈循环但又不想使其无限循环,则可以采用标准的解决方法,该布尔状态可以告诉您是否在效果钩子内调用timeChange

function Foo() {
  const [shouldUpdate, setShouldUpdate] = useState(false);
  const [time, setTime] = useState(...);

  useEffect(() => {
    if (shouldUpdate) {
      setShouldUpdate(false);
      setTime(...);
    }
  }, [shouldUpdate, time]);
  return whatever;
}

然后setShouldUpdate(true)应该由实际上触发状态更改的任何对象调用。

答案 1 :(得分:1)

https://codesandbox.io/s/timepicker-8mo3x?file=/src/App.js

好吧,我想我明白了;尽管它可以工作,但可以进一步重构:

import React, { useState, useEffect } from "react";
import "./styles.css";
import { TimePicker } from "antd";
import "antd/dist/antd.css";
import moment from "moment";

const HHmm = "HH:mm";

export default function App() {
  const [time,setTime] = useState({
    start: "",
    end: "",
    duration: ""
  })
 
  const onStartChange = value => {
    if(time.start && time.end && time.duration) {
      const diffHours = value.diff(time.end, "hours");
      const diffMinutes = moment
        .utc(moment(value, HHmm).diff(moment(time.end, HHmm)))
        .format("mm");

      setTime((time)=>({
        start: value,
        end: time.end,
        duration: moment(`${diffHours} : ${diffMinutes}`, HHmm)
      }));
    } else {
      setTime((time)=>({
        start: value,
        end: time.end,
        duration: time.duration
      }))
    }

  };

  const onDurationChange = value => {
    if(time.start && time.end && time.duration) {
      const endToStartTime = moment(time.start)
          .add(value.hours(), "h")
          .add(value.minutes(), "m");

      setTime((time)=>({
        start: time.start,
        end: endToStartTime,
        duration: value
      }));
    } else {
      setTime((time)=>({
        start: time.start,
        end: time.end,
        duration: value
      }))
    }
    
  };

  const onEndChange = value => {
    if(time.start && time.end && time.duration) {
      const diffHours = value.diff(time.start, "hours");
      const diffMinutes = moment
        .utc(moment(value, HHmm).diff(moment(time.start, HHmm)))
        .format("mm");

      setTime((time)=>({
        start: time.start,
        end: value,
        duration: moment(`${diffHours} : ${diffMinutes}`, HHmm)
      }));
    } else {
      setTime((time)=>({
        start: time.start,
        end: value,
        duration: time.duration
      }))
    }
  };

  useEffect(()=>{
      if(time.start && time.end && !time.duration) {
        const diffHours = time.end.diff(time.start, "hours");
        const diffMinutes = moment
          .utc(moment(time.end, HHmm).diff(moment(time.start, HHmm)))
          .format("mm");
        setTime((time)=>({
          start: time.start,
          end: time.end,
          duration: moment(`${diffHours} : ${diffMinutes}`, HHmm)
        }));
      }

      if(time.start && !time.end && time.duration) {
        const endToStartTime = moment(time.start)
          .add(time.duration.hours(), "h")
          .add(time.duration.minutes(), "m");
        setTime((time)=>({
          start: time.start,
          end: endToStartTime,
          duration: time.duration
        }));
      }

      if(!time.start && time.end && time.duration) {
        const startToEndTime= moment(time.end)
          .subtract(time.duration.hours(), "h")
          .subtract(time.duration.minutes(), "m");
        setTime((time)=>({
          start: startToEndTime,
          end: time.end,
          duration: time.duration
        }));
      }
  },[time])

  return (
    <div className="App">
      <span>Start: </span>
      <TimePicker value={time.start} onChange={onStartChange} format={HHmm} />
      <span>Duration: </span>
      <TimePicker value={time.duration} onChange={onDurationChange} format={HHmm} />
      <span>End: </span>
      <TimePicker value={time.end} onChange={onEndChange} format={HHmm} />
    </div>
  );
}