javascript - 避免递归函数中的状态突变(Redux)

标签 javascript recursion reactjs immutability redux

我正在使用 React 和 Redux 构建一个网络应用程序,我遇到了一个关于状态不变性的问题。

我的状态看起来类似于:-

{
    tasks: [
        {
            id: 't1',
            text: 'Task 1',
            times: {
                min: 0,
                max: 0
            },
            children: [
                { id: 't1c1', text: 'Task 1 child 1', times: { min: 2, max: 3 }, children: [] },
                { id: 't1c2', text: 'Task 1 child 2', times: { min: 3, max: 4 }, children: [] }
                ...
            ]
        }
        ...
    ]
}

这定义了“任务”对象的层次结构,每个任务在“子”数组中具有零个或多个子任务。每个任务还​​有一个“times”对象,它记录了这个任务将花费的最小(“min”)和最大(“max”)时间(以小时为单位)。

我创建了一个简单的递归函数,用于遍历任务层次结构并计算和设置所有具有一个或多个子任务的总“时间”值(最小值和最大值)。因此,在上面的示例中,任务“t1”在计算后的最小时间应为 5 (2 + 3),最大时间应为 7 (3 + 4)。这个函数当然需要在本质上是递归的,因为每个任务都可以有任意数量的祖先。

(以下是凭内存写的,没有测试所以请忽略/原谅任何错别字)

function taskTimes(tasks)
{
    let result = { min: 0, max: 0 };

    if (tasks.length === 0)
        return result;

    tasks.forEach(task => {
        if (task.children.length > 0)
        {
            let times = taskTimes(task.children);
            task.times.min = times.min; // MUTATING!
            task.times.max = times.max; // MUTATING!
            result.min += times.min;
            result.max += times.max;
        }
        else
        {
            result.min += task.times.min;
            result.max += task.times.max;
        }
    });

    return result;
}

我创建的函数工作正常,但我在编写它之前就知道我会遇到一个问题:在遍历任务层次结构时,各个任务对象(特别是“时间”对象)发生了变化。这是一个问题,因为我想在 reducer 中运行它,因此我想从这个 Action 创建一个新状态而不是改变它。

因此,我的问题是,修改此系统以避免改变状态的最佳方法是什么?

我可以尝试使用 Immutable.js 在整个层次结构中强制执行不可变性,并且尝试这可能是我的下一步。另一种选择是根本不存储此数据,而是在必要时在每个组件中计算它。虽然后者可能是一个更简洁的选择,但我仍然想知道如何最好地解决这个问题,因为我可以预见必须在其他项目中做类似的事情,为了安心,我想现在就解决这个问题。

如果您能就此领域的最佳实践提出任何建议,我们将不胜感激。另外,如果我忽略了一些非常明显的东西,请善待! 提前致谢。

最佳答案

如果您的意图不是操纵状态,那么创建一个副本将是一个快速而肮脏的解决方法,如果您在计算结果时操纵它并不重要。 如果您打算使用该函数更新子任务的所有时间,一种简单的方法是创建状态的(深)副本,操作并返回它。

例如 reducer 的一个片段:

...
switch(action.type) {
  case actions.RECALCULATE_TASK_COUNT:
    var nextState = $.extend({}, state);
    taskTimes(nextState);
    return nextState;
  ...

}
...

function taskTimes(tasks)
{
    let result = { min: 0, max: 0 };

    if (tasks.length === 0)
        return result;

    tasks.forEach(task => {
        if (task.children.length > 0)
        {
            let times = taskTimes(task.children);
            task.times.min = times.min; // MUTATING!
            task.times.max = times.max; // MUTATING!
            result.min += times.min;
            result.max += times.max;
        }
        else
        {
            result.min += task.times.min;
            result.max += task.times.max;
        }
    });

    return result;
}

关于javascript - 避免递归函数中的状态突变(Redux),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37819281/

相关文章:

powershell - 尝试遍历文件夹并对每个文件执行递归操作

ruby - 使用 Nokogiri 从空标签中递归清理 XML 文档?

javascript - 如何访问 Meteor.call() 中的变量?

javascript - 如何将重复的单元格与谷歌应用程序脚本合并在一起?

javascript - 变形目标 Three.js

javascript - 如何在 HTML5 JavaScript 中检测 Joy-Con 输入/运动控件

javascript - React - prop 函数中的 prop 函数

java - 如何在Java中使用浏览器调用javascript函数?

haskell - Church-encoded 列表的 Catamorphisms

javascript - react 使用手势 useScroll 未检测到滚动事件