【问题标题】:How to avoid infinity loop when using useEffect() hooks when added dependencies?添加依赖项时使用useEffect()挂钩时如何避免无限循环?
【发布时间】:2020-10-16 13:45:30
【问题描述】:

我正在制作这样的 TimePicker

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

当我尝试更改持续时间时它会起作用

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

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

CodeSanBox Sample(这个问题有点不同,因为我改进了一些关于时刻的逻辑)

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

【问题讨论】:

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


    【解决方案1】:

    你的问题是你正在修改你的 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)应该被任何真正触发状态改变的东西调用。

    【讨论】:

      【解决方案2】:

      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>
        );
      }
      
      
      

      【讨论】:

      • onChange不起作用,我试过了,setXXX会在它改变之前得到旧值
      • 我用另一种可能的解决方案更新了我的答案;你能提供整个组件代码吗?这会有所帮助。
      • hmm,我试过了,但似乎不起作用,我复制并在问题中添加了链接,你能检查一下吗:D
      • 是的,我刚刚意识到它们是对象,因此它们不会直接等同,但它们的 ._i 属性可以。尽管如此,即使没有进行任何更改,useEffect 似乎仍在连续运行。我用vscode打开看看。
      • 看看我的最新编辑,我想我有一个工作版本。
      猜你喜欢
      • 2021-03-07
      • 2021-09-15
      • 2020-02-26
      • 2021-12-04
      • 2018-09-03
      • 1970-01-01
      • 2019-10-25
      • 2020-08-12
      • 1970-01-01
      相关资源
      最近更新 更多