Javascript React.js:更改子状态也会更改父级传递的proptype

Javascript React.js:更改子状态也会更改父级传递的proptype,javascript,html,reactjs,react-proptypes,Javascript,Html,Reactjs,React Proptypes,我遇到的问题是,当我更改子组件的状态时,父组件的状态也会更改。我将道具传递给孩子,如下所示: {this.state.users.map((user, index) => ( <UserRow key={user.id} user={user} removeUser={this.removeUser} getUserInfo={this.getUserInfo} finishFunc={thi

我遇到的问题是,当我更改子组件的状态时,父组件的状态也会更改。我将道具传递给孩子,如下所示:

{this.state.users.map((user, index) => (
      <UserRow
        key={user.id}
        user={user}
        removeUser={this.removeUser}
        getUserInfo={this.getUserInfo}
        finishFunc={this.finishChanges}
      />
    ))}
但是,当我更新这个子状态时,父状态似乎在不重新渲染的情况下更新

我通过道具显示信息,而不是状态

<div>{this.props.user.rules.length !== 0 ? this.props.user.rules.length : null}</div>
{this.props.user.rules.length!==0?this.props.user.rules.length:null}
您可以看到初始显示

当我点击“添加”或“删除”按钮时,状态显示发生变化。您可以在图像中看到变化

我很困惑,在我没有明确地将数据传递回家长的情况下,如何更改孩子的状态来更新家长的状态。我也不知道它是如何改变显示的,因为我使用的是道具,而不是状态

您可以在一个文档中看到我的所有代码。每当单击“添加新权限”和“删除权限”按钮时,您都可以看到权限编号发生更改,我不希望发生这种情况


如何避免在更改子状态时父状态自动更改?

查看沙箱后,问题是您正在将
props.user
分配给
state.rowUser
,并正在创建指向父组件值的指针。这意味着无论何时更改
This.state.rowUser
,它实际上都会更新对
props.user
的引用

解决方案

  • 使用
    constructor()
    props.user
    绑定到
    state.rowUser
    ,这是推荐的方法,另外,使用spread操作符创建传递到组件中的对象的新实例,这将确保不创建指针
  • 另外

    快速查看沙箱,我还发现了一些其他反模式,它们会给您带来一些问题

  • 您直接更新UserRow中的状态
    第76行到第84行
    然后调用setState,您应该做的是使用spread操作符克隆对象
    const tempUserRow={…this.state.UserRow}
    ,然后操作对象并通过
    setState
    将其设置为状态。这将确保您在
    shouldComponentUpdate
    中的浅层比较传递并重新呈现组件
  • 您使用的是
    shouldComponentUpdate
    ,这很好,但是您没有做任何
    React.PureComponent
    对您来说是现成的,因此您应该将
    extends React.Component
    更改为
    extends React.PureComponent

  • 问题:

    问题在于我如何更改孩子的状态
    state={rowUser:this.props.user}
    。我对数据进行了变异,并按如下方式重置状态:

    addPermission = () => {
      const tempUser = { ...this.state.rowUser };
      tempUser.permissions.push({
        property: "select permission",
        info: "type info..."
      });
      this.setState({ rowUser: tempUser });
      console.log("[UserEditingRow.js] permission added");
    };
    
    解决方案:

    直接更改数据(即
    tempUser.permissions.push({…});
    )会导致问题。我使用React帮助更具体地更新孩子的状态。因此,新功能将是:

    addPermission = () => {
      this.setState(
        update(this.state, {
          rowUser: {
            permissions: {
              $push: [{property: 'select rule', info: ''}],
            },
          },
        }),
      );
      console.log('[UserEditingRow.js] rule added');
    };
    

    使用immutability helper设置状态允许子组件的状态发生更改,而无需更新父组件的状态,并解决了我的问题。

    用户是一个对象,我根据“this.props.user.rules…”假设用户是一个对象,因为您将引用传递给一个对象,所以在任何地方更新它都会影响使用它的任何组件。当然,从你发布的代码片段中我看不出你的父母是如何使用它的,所以我不得不猜测。我有一个沙箱,里面有所有的代码:,如上所述。是的,我想我是对的。我确保将权限数组复制到新状态。现在,由于props.user.permissions数组和state的permissions数组不再是相同的引用,因此添加权限不会更新anything@DevinFields这就是我在寻找不受孩子影响的父母。但是,我似乎无法在UserRow组件中添加或删除权限。明白了,谢谢您的回复。我已经对沙盒进行了上述更改:,但父对象的状态仍在更改。我还不得不更改
    …props.user
    的语法,因为它给了我一个意外的字符错误。这让我很困惑!我将继续查看沙箱,但无法确定父组件是如何被修改的,从而导致组件重新加载。我相信我在对该问题的评论中回答了这个问题。根据Max的回答,您只需要克隆权限对象。rest语法可能会克隆最外层的对象,但是如果外壳中有一个共享引用,我认为它不会被克隆。
    addPermission = () => {
      const tempUser = { ...this.state.rowUser };
      tempUser.permissions.push({
        property: "select permission",
        info: "type info..."
      });
      this.setState({ rowUser: tempUser });
      console.log("[UserEditingRow.js] permission added");
    };
    
    addPermission = () => {
      this.setState(
        update(this.state, {
          rowUser: {
            permissions: {
              $push: [{property: 'select rule', info: ''}],
            },
          },
        }),
      );
      console.log('[UserEditingRow.js] rule added');
    };