在没有调用setState的情况下反应状态更新?

时间:2017-10-12 11:00:19

标签: reactjs

我使用React创建待办事项列表应用程序。我的州有项目,也删除了项目,以便可以恢复:

  state = {
    items: ['item 1', 'item 2', 'item 3'],
    deletedItems: ['item 4', 'item 5']
  };

我删除项目的功能因此必须从项目状态中删除该项目并将其置于deletedItems状态。

 removeItem(number) {
    const itemsState = this.state.items;
    const deletedItemsState = this.state.deletedItems;

    const itemToBeDeleted = itemsState[number];
    deletedItemsState.push(itemToBeDeleted);

    itemsState.splice(number, 1);
    this.setState({ items: itemsState });
  }

上面的代码有效,但我很困惑为什么在没有调用setState的情况下更新了deletedItems状态。我不遵循这里的最佳做法吗?

2 个答案:

答案 0 :(得分:3)

  

我不遵循这里的最佳做法吗?

不。问题是你没有像你可能认为的那样处理临时变量,而是直接改变deletedItems。基本上是:

const itemsState = this.state.items;
const deletedItemsState = this.state.deletedItems;

这些行不会创建相应状态变量的副本,而只会创建对它们的引用。

所以当你这样做时

deletedItemsState.push(itemToBeDeleted);
你直接改变了状态!这是禁忌,你可能已经知道了。

为什么会这样?

要理解这一点,您必须了解Primitive和Compound值之间的分配有不同的作用。

例如:

var x = y;
如果x是字符串,数字,布尔值,空值或未定义,则

y将复制y的值。但是,如果它是一个对象,则数组或函数x将存储其引用。

解决方案:

您需要做的是创建它们的浅表副本。像这样:

const itemsState = this.state.items.slice();
const deletedItemsState = this.state.deletedItems.slice();

这将使itemsStatedeletedItemsState分别与this.state.itemthis.state.deletedItems分开。

演示:

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = {
      items: ['item 1', 'item 2', 'item 3'],
      deletedItems: ['item 4', 'item 5']
    };
  }

  removeItem = (number) => {
    const itemsState = this.state.items;
    const deletedItemsState = this.state.deletedItems;

    const itemToBeDeleted = itemsState[number];
    deletedItemsState.push(itemToBeDeleted);

    itemsState.splice(number, 1);
    this.setState({
      items: itemsState
    }, () => {
      console.log(this.state);
    });
  }

  render() {
    return (
      <div>
        Items:
        <ul>
          {this.state.items.map((item, i) => <li>{item} <button onClick={this.removeItem.bind(this, i)}>Remove</button></li>)}
        </ul>
        Deleted Items:
         <ul>
          {this.state.deletedItems.map(item => <li>{item}</li>)}
        </ul>
      </div>
    );
  }
}

ReactDOM.render( < MyApp / > , document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

答案 1 :(得分:0)

这不是处理应用中状态更改的正确方法。

例如,这一行

const itemsState = this.state.items;

它创建一个指向this.state.items的变量itemsState。因此,您对itemsState所做的任何更改都将在您的state.items中进行更改。这会改变您的数据。

最佳做法是使用诸如immutability-helper之类的库来进行状态更新,或者将状态对象克隆到变量中,然后对它们执行突变。完成后,最后使用setState()更新状态。

以下是一段代码片段,介绍如何使用 Object.assign()进行克隆。如您在代码段中所见,这不会改变原始状态变量。

&#13;
&#13;
var state = {
    items: ['item 1', 'item 2', 'item 3'],
    deletedItems: ['item 4', 'item 5']
  };
  
function removeItem(number) {
    const itemsState = Object.assign([], this.state.items);
    const deletedItemsState = Object.assign([], this.state.deletedItems);

    const itemToBeDeleted = itemsState[number];
    deletedItemsState.push(itemToBeDeleted);

    itemsState.splice(number, 1);
    //this.setState({ items: itemsState });
    
    console.log(state);
  }
  
removeItem(1);
&#13;
&#13;
&#13;