如何正确更新对象的状态(ReactJS + Redux)?

时间:2016-06-29 22:57:43

标签: javascript reactjs textfield redux react-jsx

所以我有两个输入字段:

  <TextField onChange={this.handleUserUsername.bind(this)}
    value={this.props.userInfo.username}
  />

  <TextField onChange={this.handleUserEmail.bind(this)}
    value={this.props.userInfo.email}
  />

一旦输入,我想将它们存储在名为'userInfo'的对象的状态中,如下所示:

  handleUserUsername(event){
    this.props.actions.updateUsername(event.target.value)
  }

  handleUserEmail(event){
    this.props.actions.updateEmail(event.target.value)
  } 

并且动作创建者是:

  updateUsername: function(eventValue){
    return {
      type: 'UPDATE_USERNAME',
      username: eventValue
    }
  },

  updateEmail: function(eventValue){
    return {
      type: 'UPDATE_USERNAME',
      email: eventValue
    }
  }

和减速器是:

function(userInfo={}, action){
  switch(action.type){
    case 'UPDATE_USERNAME':
      return {
        username: action.username
      }

    case 'UPDATE_EMAIL':
      return {
        email: action.email
      }

但是一旦将文本输入到用户名TextField中,它就会显示在TextField中输入的内容,并正确存储“用户名”的“Z”并记录以下内容(在示例中,输入“Z”):< / p>

enter image description here

然后,如果将此示例中的文本“M”输入到电子邮件TextField中,则当用户名状态保持为Z时,用户名状态将变为“未定义”,并且甚至不会存储电子邮件状态:

enter image description here

在示例的日志中,我希望看到的是:

userInfo: Object
    username: "Z"
    email: "M"

我正在使用以下内容来访问状态和操作:

function mapStateToProps(state) {
  return state
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch)
  }
}

为什么不能正确存储'userInfo'对象的状态?更新这样一个对象的正确方法是什么?最终我想将'userInfo'对象存储到一个名为users的数组中,它将存储所有用户信息。

编辑**

当我在用户名文本字段中输入“q”时,显示如下:

enter image description here

然后当我在密码文本字段中输入'm'时,两者现在都变为未定义:

enter image description here

编辑2 **

const registerReducer = function(userInfo={}, action){
  switch(action.type){
    case 'UPDATE_USERNAME':
      return {
        ...userInfo,
        username: action.username
      }

    case 'UPDATE_EMAIL':
      return {
        ...userInfo,
        email: action.email
      }

2 个答案:

答案 0 :(得分:1)

在您的示例中,您错过了应该返回现有状态以及因操作而发生的更改,从而更改以前的状态。

这就是减速机的作用:

  

操作描述了事情发生的事实,但未指定应用程序状态如何响应变化。这是减速器的工作。

您需要做的是始终返回一个新的状态对象,该状态对象是将reducer应用于发生的特定操作的旧状态对象的结果。

而不是

case 'SOME_ACTION_TYPE':
  return {
    someProperty: 'theNewValue'
  }

你应该做的是:

case 'SOME_ACTION_TYPE':
  return {
    ...state, // notice how I create a new object, but preserve all of the old
    propertyThatIChangedInMyReducer: 'theNewValue' // this will override the old
  }

基本上,您使用reducer,应用您对发生的特定操作所做的更改并返回该新对象,该对象还包含未更改的状态树的其他部分。你错过了返回另一部分。

如果让我们说你有一个密码字段,你会以类似的方式对待,你会做这样的事情:

case 'UPDATE_PASSWORD':
  return {
    ...state, //preserve current state, apply changes to it below
    password: action.password,
  }; 

答案 1 :(得分:0)

我认为问题在于您创建的更新状态对象包含仅有关已更改字段的信息。由于其中一个Redux的principles,它的状态应该是不可变的,每次你想要更新它时,你应该创建一个需要更改的新状态对象。

在您的情况下,您应该在创建新状态对象时返回两个字段(即使其中一个没有更改):

function(userInfo={}, action){
  switch(action.type){
  case 'UPDATE_USERNAME':
    return {
      username: action.username,
      email: userInfo.email     // userInfo is current state, we should save old value
    }

  case 'UPDATE_EMAIL':
    return {
      username: userInfo.username,  // userInfo is current state, we should save old value
      email: action.email      
    }

通过这种方式,您可以创建新状态,其中包含未更改的字段(具有相同的值)和已更改的字段(具有新值)

编辑:

更多例子:

假设您的申请状态如下:

oldState = {
    email: "oldValue",
    userName: "oldValue"
}

然后,如果您尝试更改电子邮件,则reducer应按以下方式返回状态对象:

return Object.assign({}, oldState, {
    email: "newValue"
})

上面的代码将生成基于旧状态的新状态对象并更改新状态中的电子邮件属性。 Object.assign

因此,您的新州将如下所示:

{
    email: "newValue",
    userName: "oldValue"    
}

在你的例子中,reducer返回状态对象如下:

{
    email: "newValue",
}

因此redux无法理解userName的当前值是什么。

相关问题