将redux与组件状态一起反应



在react UI中,我有一个表组件。您可以通过单击编辑按钮编辑表格的一行,也可以通过单击"新记录按钮"添加新记录。单击编辑按钮时,会触发一个redux操作,该操作获取行并设置模式对话框的可见属性。当单击"新记录按钮"时,会触发一个操作,创建一个新的空数据项,并触发相同的模式对话框。在模态对话框中,我有几个带有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,用于从props中读取。。。

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

有人能解释一下这个例子中redux状态如何与组件状态和道具一起发挥作用吗?

在redux或react中,您不应该直接写入props,因为您应该将您的props保持为不可变的。Redux迫使我们使用不可变状态,因为state是应用程序的真理来源。如果对状态的引用发生了更改,那么只有您的应用程序才应该呈现。如果你要改变你的状态(对象),那么引用就不会改变,你的应用程序也不知道某些状态是否已经改变。React/Redux不会自动为您提供只读对象。你可以随时对它们进行变异,但正如我告诉你的那样,这可能会导致你的应用程序不知道何时重新渲染的问题如果你想固有地拥有这个只读属性,你可能应该使用immutable.js

关于你的第二个问题,你必须将道具复制到组件的状态,以及你应该在哪里进行。你应该在组件的构造函数中进行,你应该使用免疫助手

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);

行动中:

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

在减速器中:

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

通过这种方式,你的状态将是纯净的,你不必担心免疫性。

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

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

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

一旦你开始使用redux,就很容易犯将组件的整个状态转移到减速器的错误,我已经去过那里,不久前就停止了:)

相关内容

最新更新