【问题标题】:How to properly update state of an object (ReactJS + Redux)?如何正确更新对象的状态(ReactJS + Redux)?
【发布时间】:2016-06-29 22:57:43
【问题描述】:

所以我有两个输入字段:

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

reducer 是:

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

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

但是一旦文本被输入到用户名 TextField 中,它会显示在 TextField 中输入的任何内容,并正确存储“用户名”的“Z”并记录以下内容(在示例中,输入了“Z”):

然后,当在本例中将文本“M”输入到电子邮件 TextField 中时,用户名状态在应保持为 Z 时变为“未定义”,甚至不存储电子邮件状态:

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

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

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

function mapStateToProps(state) {
  return state
}

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

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

编辑**

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

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

编辑 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
      }

【问题讨论】:

    标签: javascript reactjs textfield redux react-jsx


    【解决方案1】:

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

    这就是减速器的作用:

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

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

    代替

    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,
      }; 
    

    【讨论】:

    • 非常感谢您的澄清!我接受了您的建议并尝试实施它,在原始帖子 EDIT 2 ** 中提供,但出现以下错误:Unexpected token (5:8) at where '...' started, and can't to figure out what's wrong .
    • 这意味着你没有使用 ES6。我强烈建议您切换到使用它,因为您会在 React 和 Redux 上找到的大多数示例和资源都将使用它。如果不能或您不想这样做,您可以使用Object.assign 来保留在您的状态下保持不变的旧属性。
    • 如果是这样的话,我会更喜欢 ES6。有没有办法告诉我使用的是哪个版本?似乎“...”在某些情况下有效,但在其他情况下无效,所以想确认一下。另外我该如何切换到 ES6?
    • @ElodSzopos 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 为什么要在reducer 中每次都返回一个新的状态对象?什么是专业人士/专业人士?为什么不只更新当前状态?是因为您可以访问应用程序的状态历史以及迄今为止所做的所有更改吗?
    • @tonix redux 背后的想法是你有单向数据流,你的 reducer 是纯函数(没有副作用)。变异状态或道具可以并且将导致您的应用程序行为不端和/或出错。 redux 的基本原则不能被打破,库依赖它。您将受益于能够记录动作、重播动作、时间旅行、拥有可靠的数据源,这使得错误可预测且易于理解。希望对您有所帮助。
    【解决方案2】:

    我认为问题在于您创建了更新的状态对象,其中仅包含有关已更改字段的信息。由于 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 应该通过以下方式返回 state 对象:

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

    上面的代码会在旧状态的基础上产生新的状态对象,并在新状态下改变email属性。 Object.assign

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

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

    在您的示例中,您的减速器返回状态对象如下:

    {
        email: "newValue",
    }
    

    所以 redux 无法理解 userName 的当前值是什么。

    【讨论】:

    • 我用新日志更新了 EDIT ** 下的原始帖子。它现在显示对象状态电子邮件,但是一旦第二个文本字段接收到任何文本,它们都会变得未定义。
    • 但是我的回答对你来说仍然是实际的,正如下面提到的@ElodSzopos,在reducer 中你应该在每次通过操作进行更改时返回整个状态对象(包括电子邮件、用户名、密码)跨度>
    • 我尝试像我在 EDIT 2 ** 中提供的那样实现,但出现以下错误:Unexpected token (5:8) at where '...' started, and can't to figure out怎么了。
    • @JoKo 试试我的例子,它不应该需要 ES6 标准。 ...是Spread operator,它是在ECMAScript 6中添加的,它是javascript实现的。所有官方redux教程都写在这个版本上,你最好弄清楚你用的是什么版本。
    猜你喜欢
    • 2019-07-05
    • 2018-04-03
    • 2020-11-11
    • 1970-01-01
    • 2016-11-27
    • 2022-10-15
    • 2021-01-24
    • 2019-05-15
    • 1970-01-01
    相关资源
    最近更新 更多