ReactJs:不可变数据

时间:2017-05-16 22:08:34

标签: javascript reactjs immutability

我正在做官方的ReactJs教程,我开始对不变性的含义和应用感到困惑。

我认为的方式的一个例子。想象一下,我在构造函数this.state = { dog: some data}中有一个对象,然后我调用一个设置this.setState = {dog: another data}的handleClick函数。由于我使用setState来更新值,旧引用仍然是不可变的,我可以知道发生了什么变化。

但是在教程中,当他们复制" square"状态.slice()不更改原始数组然后应用setState新值,我感到困惑。

为什么他们需要复制数组,他们不能只在setState中引用它并改变那里的东西?原始参考文献仍然可追溯......

编辑:

@FelixKling这里:

class Board extends React.Component {
  constructor() {
    super();
    this.state = {
       squares: Array(9).fill(null),
    };
  }

  handleClick(i) {
    const squares = this.state.squares.slice();
    squares[i] = 'X';
    this.setState({squares: squares});
  }
}

他们不能把它放在setState里面,而不是复制初始数组吗?

2 个答案:

答案 0 :(得分:2)

这是一篇非常好的文章,有助于解释为什么不变性很重要:http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/

  

事实上,React.js不需要了解究竟改变了什么。它需要知道的是状态是否完全改变

     

虽然不可变性不能为究竟发生了什么改变问题提供更简单的答案,但它为提供了一个很好的答案,它是否完全改变了问题。

反应,顾名思义'反应'改变状态,并在状态改变时重新渲染组件。因此,对于React来说,知道状态是否变化很重要,遗憾的是,变异对象会使得比较变得困难。

考虑这个例子:

var a = 6; // numbers are primatives in javascript and compare by value
var b = 6; 
var c = a; // 6

// in this case 
a === b // true
b === c // true, 6

但你不能通过引用比较对象来做同样的事情,也就是说它们指向内存中的同一点。

var objA = { name: 'Sally' };
var objB = { name: 'Sally' };
var objC = objA;

// these are not the same object
objA.name === objB.name // true, 'Sally'
objA === objB // false, they reference different objects even though the keys and values are the same
objA === objC // true, they reference the same point in memory

当您尝试修改值时,更容易说明React中出现的问题。

让我们再次以我们的数字为例进行一些修改:

c = 14;
a === b // true, still 6, the value of 'a' is still 6
a === c // false, 6 ≠ 14

这意味着,我们已经改变了c的价值,它现在应该是不同的。我们现在修改我们的对象。

objA.age = 30; // give objA an age property    
objB.age = 35; // give objB an age property
objC.age = 40; // give objC an age property, actually changes objA

// now try equality
objA === objB // false, we already know this
objA.age === objB.age // false, as expected
objA.age === objC.age // true, possibly unexpected as it looks like we are comparing 30 to 40, but in fact our change to objC modified objA
objA === objC // true both have age set to 40

因此,如果我们每次都给React一个新的状态,而不是改变它,那么它是否应该更容易知道它是否应该重新渲染。

你可能想知道,为什么反应并不只是存储状态的副本并将其与变异状态进行比较。也许你并不想知道这一点,但似乎这是React / Redux可以处理的东西,然后我们可以自由地改变状态,让我们更容易。

事实证明,比较两个对象是一项非常密集的任务,首先你必须确保它们都具有相同的键,并且这些键具有相同的值 - 看起来并不难 - 但是然后你意识到对象可以包含许多其他嵌套对象,这个比较变得递归,检查每个嵌套对象。

传递一个新的状态要容易得多,以便React可以立即告诉它已经改变了。

除此之外,生成新状态还允许您定时旅行。通过跟踪和存储以前的状态,如果你选择。

答案 1 :(得分:0)

在教程中,他们详细说明了不变性的重要性: https://facebook.github.io/react/tutorial/tutorial.html#why-immutability-is-important

您还可以在此处阅读很多关于不变性的内容:https://facebook.github.io/immutable-js/

Immutable JS是Facebook为对象Immutability创建的库

TLDR链接:跟踪更改基本上更容易,更容易确定何时重新渲染。

关于评论的编辑:

所以你是正确的this.setState确实更新了对象而不是改变对象。

在这个例子中,我们创建一个副本而不是变异。

handleClick(i) {
  const squares = this.state.squares.slice();
  squares[i] = this.state.xIsNext ? 'X' : 'O';
  this.setState({
    squares: squares,
  });
}

在这个例子中,我们改变而不是创建副本

handleClick(i) {
  this.state.squares[i] = this.state.xIsNext ? 'X' : 'O';
  this.setState({
    squares: squares,
  });
}

对于我们没有变异的第一个例子,当React在this.setState被调用并且到达shouldComponentUpdate之后运行其生命周期事件时,这将是会发生什么

shouldComponentUpdate(nextProps, nextState) {
    // this will return true and trigger an update
    return this.state.squares !== nextState;
}

对于第二个例子,我们做了变异的地方,接下来会发生什么:

shouldComponentUpdate(nextProps, nextState) {
    // this will return false and will not trigger an update
    return this.state.squares !== nextState;
}

现在,即使我们已经改变了状态,因为我们当前的变异状态与我们的下一个状态(在this.setState中设置)相同,我们的react组件将不会更新。