【问题标题】:State doesn't update on first action while dispatching 2 sync actions in react-redux在 react-redux 中调度 2 个同步操作时,状态不会在第一个操作上更新
【发布时间】:2018-08-12 07:43:16
【问题描述】:

我正在尝试将pomodoro clock 作为 FreeCodeCamp 的练习项目。这是一个简单的时钟,只要当前计时器结束,它就会在您的会话时间和休息时间之间切换。

我在 react 中遇到了与 redux 相关的问题。这是我的应用启动时的状态。

当我单击减号或加号时,商店中的计时器和会话/休息时间将相应更新,但这并没有发生。会话/中断长度已更新,但计时器反映了先前的值。这是我增加会话时反映问题的屏幕截图

这是我负责的代码

updateSettings(type) {

    if (!this.props.isPlaying) {

        let isInvalid = false; // to show alert if user input is invalid

        if (type === 'increase-session') {
            this.props.increaseSession();
        } else if (type === 'decrease-session') {
            (this.props.session_length > 1) ? this.props.decreaseSession() : isInvalid = true;
        } else if (type === 'increase-break') {
            this.props.increaseBreak();
        } else if (type === 'decrease-break') {
            (this.props.break_length > 1) ? this.props.decreaseBreak() : isInvalid = true;
        }

        if (isInvalid) {
            alert("Times less than 1 minutes is not allowed.");
        } else {
            this.props.setTimer({
                minutes: this.props.session_length,
                seconds: 0,
                percentage: this.getTimeElapsedPercentage(this.props.session_length, 0)
            });
        }
    }
}

在上面的代码中,所有动作都是同步的,当我调用this.props.increaseSession() 时,它会调度增加会话的动作。然后我调用this.props.timer(timerObj),而此计时器对象接收到 session_length 值但此 session_length 值未更新意味着增加(如果我增加会话)。

我的问题是所有操作都是同步的,那么为什么我在调度 setTimer() 时收到旧值。任何解决此问题的建议将不胜感激。

正如 Roy 所建议的,如果我在传递给 setTimer 之前自己更新了这些值,则百分比计算不正确。这是计算进度条(计时器周围的圆形条)的函数。该函数负责设置定时器启动时和每一秒的值。虽然它最初可以通过传递this.props.session_length 正确计算百分比,但这不会是正确的 session_length。会话长度也在产生问题的getTimeElapsedPercentage 内部使用。我可以使用一些逻辑来解决这个问题,但我希望有正确的解决方案而不是变通方法。

getTimeElapsedPercentage(minutes, seconds) {
    let totalTimeInSec, timeElapsedInSec, timeRemainingInSec;

    this.props.playType === 'break' ?
        totalTimeInSec = this.props.break_length * 60 :
        totalTimeInSec = this.props.session_length * 60;

    timeRemainingInSec = minutes * 60 + seconds;
    timeElapsedInSec = totalTimeInSec - timeRemainingInSec;

    return timeElapsedInSec / totalTimeInSec * 100;
}

解决方案(解决方法 - 正确答案标记如下)

正如在 cmets 中与 Roy 讨论的那样,我知道道具只会在每次渲染后更新,因此当我使用 setTimer(timerObj) 设置计时器时,this.props.session_length 会为我提供以前的值。

所以我进入了这个解决方案。在第一次操作之后,我的 store 更新了,但 props 没有更新,所以我从我的 redux 代码中导入 store 并使用 store.getState() 来获取更新的值。我在我的setTimer(timerObj)getTimeElapsedPercentage 中使用了它来解决问题。

【问题讨论】:

    标签: reactjs redux


    【解决方案1】:

    我假设每次单击用于增加会话或中断的按钮时都会调用updateSettings(type)。在这种情况下,当函数被调用并到达this.props.increaseSession()this.props.decreaseSession() 部分时,JavaScript 引擎会调用这些函数并执行其操作。与此同时,编译器继续执行下一行并到达

    if (isInvalid) {
      alert("Times less than 1 minutes is not allowed.");
    } else {
      this.props.setTimer({
        minutes: this.props.session_length,
        seconds: 0,
        percentage: this.getTimeElapsedPercentage(this.props.session_length, 0)
      });
    }
    

    此时增加或减少会话调用尚未完成,因此this.props.session_length 的值尚未更新。这就是您看到计时器值始终落后于持续时间值的原因。

    我的建议是将设置session_length 的代码从该函数移出到用户单击开始时运行的函数。这样您就不必担心保持两个变量同步。首先,您让用户设置会话和休息时间。然后,当他们单击开始时,您将负责将这些值分配给计时器。

    如果您不想这样,我建议您将 setTimer 移到 if、else 语句中,并根据选择使用 minutes: this.props.session_length + 1minutes: this.props.session_length - 1,当您调用以下代码时。

    this.props.setTimer({
      minutes: this.props.session_length,
      seconds: 0,
      percentage: this.getTimeElapsedPercentage(this.props.session_length, 0)
    });
    

    更新:

    要解决百分比问题,您可以更新getTimeElapsedPercentage 以采用名为plusMinus 的额外参数。根据增量或减量,这是 +1 或 -1。稍加改动后,它的功能就可以如下所示。

    getTimeElapsedPercentage(minutes, seconds, plusMinus) {
        let totalTimeInSec, timeElapsedInSec, timeRemainingInSec;
    
        this.props.playType === 'break' ?
            totalTimeInSec = (this.props.break_length + plusMinus) * 60 :
            totalTimeInSec = (this.props.session_length + plusMinus) * 60;
    
        timeRemainingInSec = minutes * 60 + seconds;
        timeElapsedInSec = totalTimeInSec - timeRemainingInSec;
    
        return timeElapsedInSec / totalTimeInSec * 100;
    }
    

    在 updateSettings 中设置它的 if 语句

    let plusMinus = 0; 
    if (type === 'increase-session') {
      plusMinus = 1;
    } else if (type === 'decrease-session') {
      plusMinus = -1;
    }
    

    调用如下

    percentage: this.getTimeElapsedPercentage(this.props.session_length, 0, plusMinus)
    

    我希望这对项目有所帮助并祝你好运。

    【讨论】:

    • 您正确理解并感谢您的建议,但每当我更改设置时,它应该更新计时器。更改设置不会是预期的行为,并且计时器保持不变。我知道为什么会出现问题,但它们不是同步事件,所以我应该收到更新的值。我认为我可以通过使用一些中间件技术来解决这个问题,但首先我想知道为什么会发生这种情况,或者除了中间件之外还有什么好的解决方案。
    • 不用担心,在这个阶段你不需要中间件。这将使您的项目比需要的更复杂。我已经用替代解决方案更新了我的答案
    • 我之前尝试过这个解决方案,但是它在百分比函数上产生了问题。在我的回答中详细描述了正确的描述。
    • 是的,当您尝试保持两件事同步时,您需要涵盖由此产生的任何情况。这就是为什么我建议第一个解决方案。看看更新。它还涵盖了百分比函数,并进行了小幅更新。看看这是否有效:)
    • 这是我试图避免的解决方法,因为它会扰乱通用逻辑。我想让它们保持同步,如果没有发生这种情况,那么我将应用您的解决方案并接受您的回答。顺便说一句,感谢您宝贵的时间和背靠背的帮助答案。
    【解决方案2】:

    实际上,问题在于道具仅在渲染后更新,this.props.setTimer 正在使用渲染前的道具,因为它具有旧的 this.props.session_length 值。这可以通过使用在 props 更改时调用的 react componentDidUpdate() 生命周期来解决,然后您可以设置计时器。

    • 从您的updateSettings(type) 函数中删除setTimer()。所以道具只更新一次(即在增加会话、减少会话等)
    • 将以下代码添加到您的组件中

    这负责在道具更改时设置计时器,这意味着现在它将更新的道具传递给计时器。

    componentDidUpdate(prevProps) {
            if (this.props.session_length !== prevProps.session_length) {
                this.props.setTimer({
                    minutes: this.props.session_length,
                    seconds: 0,
                    percentage: this.getTimeElapsedPercentage(this.props.session_length, 0)
                });
            }
        }
    

    【讨论】:

    • 是的,这就是 componentDidUpdate 解决方案。很高兴看到您在应用程序中对其进行了编码。干得好!
    猜你喜欢
    • 1970-01-01
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 2019-12-04
    • 1970-01-01
    • 2016-08-14
    • 2021-03-29
    相关资源
    最近更新 更多