在反应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状态如何与此示例的组件状态和道具一起播放?
答案 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});
}
}