React状态变量自动更新,无需调用setState。

问题描述 投票:0回答:1

我现在面临着以下问题,想不通。

我在状态里面有两个变量,叫做userDetails & userDetailsCopy。在 componentDidMount 中,我正在进行 API 调用并将数据保存在 userDetails & userDetailsCopy 中。

我正在维护另一个名为userDetailsCopy的副本,以便进行比较。

我在setState里面只更新了userDetails,但是即使userDetailsCopy也在更新,而不是有旧的API数据。

以下是代码。

constructor(){
    super()
    this.state={
        userDetails:{},
        userDetailsCopy: {}
    }
}

componentDidMount(){
     // API will return the following data
       apiUserDetails : [
            {
                 'name':'Tom',
                 'age' : '28'
            },
            {
                 'name':'Jerry',
                 'age' : '20'

            }
        ]

       resp.data is nothing but apiUserDetails
    /////

     apiCall()
     .then((reps) => {
         this.setState({ 
             userDetails: resp.data,
             userDetailsCopy: resp.data
         })
     })
}

updateValue = (text,i) => {
     let userDetail = this.state.userDetails
     userDetail[i].name = text
     this.setState({ 
         userDetails: userDetail
     })
}

submit = () => {
     console.log(this.state.userDetials) // returns updated values
     console.log(this.state.userDetailsCopy) // also return updated values instead of returning old API data
}

Need a quick solution on this.
javascript reactjs immutability
1个回答
2
投票

这个问题是你认为你这样做是在做状态中对象的复制

     let userDetail = this.state.userDetails
     userDetail.name = text

但是,在Javascript中,对象不是这样复制的,而是通过引用传递的。所以userDetail在这一点上包含了对的引用。userDetails 在你的国家,当你突变了 userDetail 它去变异州的那个。

参考文献。https:/we-are.bookmyshow.comunderstanding-deep and shallow-copy-in-javascript-13438bad941c。

为了正确地将对象从状态克隆到你的本地变量,你需要这样做。

let userDetail = {...this.state.userDetails}

OR

let userDetail = Object.assign({}, this.state.userDetails)

永远记住,对象是通过引用而不是值来传递的。

EDIT:我没看清楚问题,但上面的答案还是有效的。原因是 userDetailCopy 被更新也是因为 resp.data 是通过对它们两个的引用传递的,编辑其中任何一个都会编辑另一个。


1
投票

React状态和它的数据应该被视为不可改变的。

React文档:

永不变异 this.state 直呼 setState() 之后可能会取代你所做的突变。治療 this.state 仿佛它是不可改变的。

下面是五种如何把状态当作不可变的方法。

方法#1: Object.assign和Array.concat.

updateValue = (text, index) => {
  const { userDetails } = this.state;

  const userDetail = Object.assign({}, userDetails[index]);

  userDetail.name = text;

  const newUserDetails = []
    .concat(userDetails.slice(0, index))
    .concat(userDetail)
    .concat(userDetails.slice(index + 1));

  this.setState({
    userDetails: newUserDetails
  });
}

方法#2:对象和数组展开

updateValue = (text, index) => {
  const { userDetails } = this.state;

  const userDetail = { ...userDetails[index], name: text };

  this.setState({
    userDetails: [
      ...userDetails.slice(0, index),
      userDetail,
      ...userDetails.slice(index + 1)
    ]
  });
}

办法3:不可更改性帮手

import update from 'immutability-helper';

updateValue = (text, index) => {
  const userDetails = update(this.state.userDetails, {
    [index]: {
      $merge: {
        name: text
      }
    }
  });

  this.setState({ userDetails });
};

方法#4:Immutable.js(不可改变的)。

import { Map, List } from 'immutable';

updateValue = (text, index) => {
  const userDetails = this.state.userDetails.setIn([index, 'name'], text);

  this.setState({ userDetails });
};

方法5:Immer

import produce from "immer";

updateValue = (text, index) => {
  this.setState(
    produce(draft => {
        draft.userDetails[index].name = text;
    })
  );
};

注意:选项#1和#2只做浅层克隆。因此,如果你的对象包含嵌套对象,那些嵌套对象将通过引用而不是通过值来复制。所以,如果你改变了嵌套对象,你会突变原始对象。

为了保持 userDetailsCopy 的不变性,你需要保持的是 state (和 state.userDetails 当然是)。)

function getUserDerails() {
  return new Promise(resolve => setTimeout(
    () => resolve([
      { id: 1, name: 'Tom', age : 40 },
      { id: 2, name: 'Jerry', age : 35 }
    ]),
    300
  ));
}

class App extends React.Component {
  state = {
    userDetails: [],
    userDetailsCopy: []
  };

  componentDidMount() {
    getUserDerails().then(users => this.setState({
      userDetails: users,
      userDetailsCopy: users
    }));
  }
  
  createChangeHandler = userDetailId => ({ target: { value } }) => {
    const { userDetails } = this.state;
    
    const index = userDetails.findIndex(({ id }) => id === userDetailId);
    const userDetail = { ...userDetails[index], name: value };

    this.setState({
      userDetails: [
        ...userDetails.slice(0, index),
        userDetail,
        ...userDetails.slice(index + 1)
      ]
    });
  };
  
  render() {
    const { userDetails, userDetailsCopy } = this.state;
    
    return (
      <React.Fragment>
        {userDetails.map(userDetail => (
          <input
            key={userDetail.id}
            onChange={this.createChangeHandler(userDetail.id)}
            value={userDetail.name}
          />
        ))}
        
        <pre>userDetails: {JSON.stringify(userDetails)}</pre>
        <pre>userDetailsCopy: {JSON.stringify(userDetailsCopy)}</pre>
      </React.Fragment>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<div id="root"></div>
<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>
© www.soinside.com 2019 - 2024. All rights reserved.