react-redux与组件状态一起

时间:2017-11-19 08:25:09

标签: react-redux

在反应UI中,我有一个表组件。您可以通过单击编辑按钮来编辑表格的一行,也可以通过单击"新记录按钮"来添加新记录。单击编辑按钮时会触发redux-action,该操作将获取行并设置模式对话框的可见属性。当"新记录按钮"单击触发的操作,该操作将创建一个新的空数据项,并触发相同的模式对话框。 在模态对话框中,我有几个带有onChange方法的文本组件。 在这个onChange方法中写入数据项。 当用户单击保存按钮时,编辑的dataItem将保存到数据库中。

所以我的代码如下:

const mapStateToProps = (state) => ({
 dataItem: state.datItemToEdit || {}, 
...
});

...

handleTextChange(event) {
  const {
    dataItem 
  } = this.props;

  const id = event.target.id;
  const text = event.target.value;

  switch (id) {
    case 'carId': {
      dataItem.carId = text;
      break;
    }
    ...
  }
  this.forceUpdate();
}
...
<TextField 
  ...
  onChange={event => this.handleTextChange(event)}
/>

关于这种方法,我有几个问题。首先我不明白为什么在handleTextChange中我们可以写入dataItem。它显然工作。 dataItem.carId在示例代码中设置,但我想

const {dataItem} = this.props;

给我们一个本地只读变量dataItem,只是为了从道具中读取...

我认为接下来的事情是糟糕的设计。在阅读了一本关于反应的书后,我认为我们不应该写道具而只是设置一个州。 在我的例子中,我从redux-state获取dataItem。 mapStateToProps将它映射到组件的(只读)道具,对吧?!但我想编辑它。所以我必须将它复制到我的组件状态? 但是去哪里呢? 一旦进入我的组件的状态,我可以简单地为各种文本字段调用this.setState,组件将呈现,我可以弃用forceUpdate(),对吧?!

有人可以解释一下redux状态如何与此示例的组件状态和道具一起播放?

2 个答案:

答案 0 :(得分:0)

当您希望应用中的不同,不相关的组件知道并分享特定状态时,您应该只使用redux。 例如 - 当用户登录到您的应用程序时,您可能希望所有组件都知道该用户,因此您将不同的容器连接到用户reducer,然后将用户传播到组件。 听起来在这种情况下,您有一个使用内部状态的经典用例。 您可以使用所有TextFields的父级来维护所有行,按索引编辑它们等等。

一旦你开始使用redux,很容易犯下将组件的整个状态转移到reducer的错误,我去过那里并且不久前停止了这样做:)

答案 1 :(得分:0)

在redux或反应中,你不应该直接写道具,因为你应该把你的道具保持为immutable。 Redux强制我们使用不可变状态,因为state是应用程序的真实来源。如果对状态的引用发生更改,则只应呈现您的应用。如果你改变你的状态(对象),那么引用就不会被改变,你的应用程序也不知道某个状态是否已被改变。 React / Redux不会自动为您提供只读对象。您可以随时改变它们,但正如我告诉您的那样,它可能导致您的应用程序无法知道何时重新渲染的问题。如果您想要拥有此只读属性,那么您应该使用immutable.js

关于您的第二个问题,您必须将道具复制到组件的状态以及应该在哪里执行。您应该在组件的构造函数中执行它,并且应该使用immutibility helper

import React from React;
import update from 'immutibility-helper';

class Modal extends React.Component {
    constructor(props){
        this.state = {
            dataItem: dataItem,
        };
    }

    ...other methods

    handleTextChange(event) {
      const {
       dataItem 
      } = this.props;

     const id = event.target.id;
     const text = event.target.value;

     switch (id) {
         case 'carId': {
             this.props.updateItem(this.state.dataItem, text); //fire a redux action to update state in redux
             this.setState(update(this.state, {
                 dataItem: {
                     carId: {$set: text},
                 }
             });
             break;
         }
         ...
      }

   }
}

在这种情况下你不必做forceUpdate因为对状态的引用会改变,组件将重新呈现自己。

此外,您可以在您的应用中使用forceUpdate,但我个人认为这不是一个好主意,因为当React/Redux通过forceUpdate为您提供状态流时你正在打破这种流动。

最后一个问题是减少和反应状态如何一起发挥作用。这也是一个选择问题。如果我有一个应用级别的状态,例如,在你的情况下,你有一些应用程序级数据,你应该把它放在你的redux状态,如果你有一个组件级别的东西,比如打开一个模态或打开第三个窗格。这是我遵循的惯例,但这实际上取决于您如何利用react和redux状态。

此外,在上面的代码中,我也将redux状态置于组件状态(因为您询问了放置它的位置),但理想情况下,您应该触发redux操作并以redux状态更新。通过这种方式,您将限制自己在react和redux中的状态重复。

import React from React;
import {updateItem} from './actions';

class Modal extends React.Component {

    ...other methods

    handleTextChange(event) {
      const {
       dataItem 
      } = this.props;

     const id = event.target.id;
     const text = event.target.value;

     switch (id) {
         case 'carId': {
             this.props.updateItem(this.props.dataItem, text); //fire a redux action to update state in redux
             break;
         }
         ...
      }

   }
}

const mapStateToProps = (state) => ({
    dataItem: getDataItem(state), //get Data Item gets Data from redux state
});

export default connect(mapStateToProps, {updateItem: updateItem})(Modal);

in Actions:

updateItem = (dataItem, text) => dispatch => {
    dispatch({type: 'UPDATE_ITEM', payLoad: {dataItem, text});
};
在Reducer中

export default (state = {}, action) => {
    switch(action){
        case 'UPDATE_ITEM': {
            return {
                ...state,
                dataItem: {
                    ...action.dataItem,
                    carId: action.text,
                }
            };
        }
    }
}

通过这种方式,你的国家将是纯洁的,你不必担心不可移植性。

修改 由于构造函数只会被调用一次,因此您应该使用componentWillReceiveProps,这样无论何时渲染组件,您都会获得组件的下一个更新道具。您可以检查dataItem的carId是否相同,然后更新状态。

componentWillReceiveProps(nextProps){
    if(nextProps.dataItem.carId !== this.props.dataItem.carId){
        this.setState({dataItem: nextProps.dataItem});
    }
}