繁体   English   中英

React state 变量自动更新,无需调用 setState

[英]React state variable updates automatically without calling setState

我面临以下问题,无法弄清楚。

我在 state 中有两个变量,称为 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.

这样做的问题是您认为您正在通过执行此操作在 state 中复制 object

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

但是,在 Javascript 中,对象不是这样复制的,它们是通过引用传递的。 因此,此时的 userDetail 包含对userDetails中的 userDetails 的引用,当您改变userDetail时,它会改变 state 中的 userDetail。

参考: https://we-are.bookmyshow.com/understanding-deep-and-shallow-copy-in-javascript-13438bad941c

要将 object 从 state 正确克隆到本地变量,您需要改为执行以下操作:

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

OR

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

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

编辑:我没有正确阅读问题,但上述答案仍然有效。 userDetailCopy也被更新的原因是因为resp.data是通过引用传递给它们的,并且编辑它们中的任何一个都会编辑另一个。

React state 和它的数据应该被视为不可变的。

反应文档

永远不要直接改变this.state ,因为之后调用setState()可能会替换你所做的突变。 this.state视为不可变。

以下是如何将 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:Object 和阵列扩展

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:沉浸式

import produce from "immer";

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

注意:选项#1 和#2 只进行浅层克隆。 因此,如果您的 object 包含嵌套对象,则这些嵌套对象将按引用而不是按值复制。 因此,如果您更改嵌套的 object,您将改变原始的 object。

要保持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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM