简体   繁体   中英

How to properly update state of an object (ReactJS + Redux)?

So I have two input fields:

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

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

once entered, I would like to store them in the state of an object named 'userInfo', like so:

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

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

and the action creators are:

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

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

and the reducer is:

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

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

But once texts are inputted into username TextField, it displays whatever it is typed in the TextField and correctly stores 'Z' for the 'username' and logs the following (in the example, 'Z' was typed in):

在此输入图像描述

Then when a text, 'M' in this example, is entered into the email TextField, the username state becomes 'undefined' when it should stay as Z, and email state is not even stored:

在此输入图像描述

In the log for the example, what I am hoping to see is:

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

And I am using the following to access state and actions:

function mapStateToProps(state) {
  return state
}

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

Why isn't it correctly storing the state for the 'userInfo' object? What would be the proper way to update such an object? Eventually I want to store the 'userInfo' object into an array called users, where it would store all the user information.

EDIT **

When I entered 'q' in the username textfield, the following shows:

在此输入图像描述

Then when I enter 'm' in the password textfield, both become undefined now:

在此输入图像描述

EDIT 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
      }

In your example you missed that you should be returning the existing state along with the changes that happened as result of your action, altering the previous state.

This is what a reducer does:

Actions describe the fact that something happened, but don't specify how the application's state changes in response. This is the job of a reducer.

What you need to do is to always return a new state object that is the result of applying your reducer to the old state object for the particular action that happened.

Instead of

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

What you should do is:

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
  }

Basically, you take the reducer, apply the changes that you would for the specific action that happened and return that new object, that also contains the other part of the state tree that remained unchanged. You missed returning the other part.

If let's say you had a password field too that you would treat in a similar manner, you would do something like this:

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

I think the problem is that you create updated state object that contains information only about changed fields. Due to one of the Redux's principles its state should be immutable and each time you want to update it you should create new state object with needed changes.

In your case, you should return both fields when creating new state object (even if one of them didn't change):

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      
    }

By this you create new state which contains fields that didn't changed(with same values) and fields that changed(with new values)

EDIT :

more examples:

assume that state of your application is the following:

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

Then if you trying to change email, you reducer should return state object in the following way:

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

Code above will produce new state object basing on the old state and change email attribute in the new one. Object.assign

As a result your new state will look like this:

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

In your example your reducer return state object like this:

{
    email: "newValue",
}

So the redux cannot understand what current value for userName is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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