状态更新事件处理程序

时间:2019-01-02 02:47:49

标签: javascript reactjs

状态得到更新,而无需显式设置

我偶然遇到了这个,这让我感到困惑。据我了解,您可以按照以下步骤进行操作:

1-创建当前状态的副本 2-修改副本 3-将修改后的副本分配给状态,从而对其进行更新。

这就是我的方法,而且效果很好。

handleChange(index, event) {
    const {name, value} = event.target;
    this.setState((prevState) => {
        const newState = [...prevState.items];
        newState[index][name]= value;
        return{items: newState};
    });

但是奇怪的是,下面的代码也可以正常工作,但是不必像上面那样在最后一行中显式地分配它。只要我更新了不是它的引用的副本,它就会更新。

handleChange(index, event) {
    const {name, value} = event.target;
    this.setState((prevState) => {
        const newState = [...prevState.items];
        newState[index][name]= value;

        return{}; //it works as long as I have the brackets.
    });

2 个答案:

答案 0 :(得分:1)

从您问题中的代码来看,您的州似乎具有类似于以下内容的结构:

state = {项目:[{name1:'index0Value1',name2:'index0Value2'},{name1:'index1Value1',name2:'index1Value2'}]};

其中items是对象数组。

当您这样做:

const newState = [...prevState.items];

您仅在复制数组,但是newState数组中的对象仍与原始状态共享。因此,您可以在不影响原始对象的情况下将新对象推送到数组中,但是在这样做时:

newState[index][name]= value;

您正在以原始状态和新状态更改该对象。运行下面的代码片段以查看演示。

const prevState = { items: [{name1: 'index0Value1', name2: 'index0Value2'}, {name1: 'index1Value1', name2: 'index1Value2'}] };

document.getElementById('origbefore').innerHTML = 'prevState before='+JSON.stringify(prevState);

const newItems = [...prevState.items];
newItems[0]['name1']='tryingToOnlyChangeNewState';
const newState = { items: newItems };

document.getElementById('origafter').innerHTML = 'prevState after='+JSON.stringify(prevState);
document.getElementById('new').innerHTML = 'newState='+JSON.stringify(newState);
<div id="origbefore"></div>
<div id="origafter"></div>
<div id="new"></div>

因此,在两个示例中,您的分配都在更改prevState,并且由于setState支持传递与先前状态合并的部分状态,因此当您返回空对象时,React会将其与已变异的先前状态合并状态,然后重新渲染结果。

答案 1 :(得分:1)

您正在使用spread syntax复制状态。传播语法仅执行浅表复制。因此,当您更改新对象的嵌套属性时,它也会同时更改原始对象的嵌套属性。这就是第二个代码起作用的原因。您的变异状态与当前状态合并,即当前状态,您会看到变异的结果。不要改变您的状态。

大多数时候,您应该使用类似mapfilter之类的方法来突变新复制的对象,并且仅更改vale而不会改变原始状态。因此,您可以将这些方法与扩展语法或Object.assign混合使用,但不要直接更改复制的方法。

由于我们不知道您的数据的确切形状,因此我只是在模仿以下代码。如果您有索引,则可以使用此技术。

class App extends React.Component {
  state = {
    items: [{ foo: "a foo" }, { bar: "a bar" }, { baz: "a baz" }],
  };

  handleChange(index, event) {
    const { name, value } = event.target;
    const { items } = this.state;
    const newItems = Object.assign([], items, {
      [index]: { ...items[index], [name]: value },
    });
    this.setState({ items: newItems });
  }

  render() {
    return (
      <div>
        {this.state.items.map((item, index) => (
          <input
            name={Object.keys(item)}
            key={Object.keys(item)}
            index={index}
            onChange={(event) => this.handleChange(index, event)}
          />
        ))}
        <div>
          {this.state.items.map((item) => (
            <p key={Object.values(item)}>{Object.values(item)}</p>
          ))}
        </div>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

但是,如果您像在第二个示例中那样更改此代码,则会发现它不起作用,因为我们不在此处更改原始状态。

class App extends React.Component {
  state = {
    items: [{ foo: "a foo" }, { bar: "a bar" }, { baz: "a baz" }],
  };

  handleChange(index, event) {
    const { name, value } = event.target;
    const { items } = this.state;
    const newItems = Object.assign([], items, {
      [index]: { ...items[index], [name]: value },
    });
    // this.setState({ items: newItems });
    this.setState({});
  }

  render() {
    return (
      <div>
        {this.state.items.map((item, index) => (
          <input
            name={Object.keys(item)}
            key={Object.keys(item)}
            index={index}
            onChange={(event) => this.handleChange(index, event)}
          />
        ))}
        <div>
          {this.state.items.map((item) => (
            <p key={Object.values(item)}>{Object.values(item)}</p>
          ))}
        </div>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>