在组件更新Reduce存储中反应本机setState
我已经在多个web应用程序中使用了redux。目前,我正在使用Redux开发我的第一个React Native应用程序。在组件更新Reduce存储中反应本机setState
我遇到了一个很奇怪的问题。
我创建了一个存储并将其传递给将组件作为子组件呈现的组件。 (react-redux的基本用法)
在应用程序中,我有一个连接组件。它将从商店收到的数据传递给导航路线,同时呼叫navigator.push(路线)。此路线上的组件不是连接的组件。它收到的道具和存放道具的状态。道具不是简单的文字,而是对象/数组。 根据用户交互,此组件通过setState更新其状态。 此操作直接更新商店。
我是不是应该通过设置从零售店收到的零部件状态道具matchStateToProps?尽管如此,setState发生在不同的组件中。商店不应该简单地更新自己。
我很困惑。请帮忙。
(如果问题不明确或混淆,我会从我的代码添加相关代码段)
编辑1:
Here is a fiddle which conveys my problem
const intialState = {
0: {
orgId: 0,
peopleInfo: {
0 : {
pID: 0,
name: 'parent',
children: [
{
userID: 1,
name: 'abc',
},
{
userID: 2,
name: 'xyz',
},
]
}
}
}
}
function reducer (currentState, action) {
currentState = intialState;
console.log(currentState[0].peopleInfo[0]); // priniting the store every time an action is dispatched
// NO CHANGES TO THE STORE WHEN ACTION IS DISPATCHED
return currentState;
}
// Create Store
var store = Redux.createStore(reducer);
// action creator which will simply trigger the reducer
function save(){
return {
type: 'SAVE'
}
}
// Presentational Components (No state, only props and render)
var OrgsContainer = React.createClass({
render() {
return (
<div>
<div>
<div>1. Open console first</div>
<div>2. Change parent name - no change in the name property for the record on the store </div>
<div>3. Change any child - it changes the property on the store even if there is no implementation in the reducer</div>
<br />
</div>
<PeopleContainer people ={this.props.peopleInfo} saveAction = {this.props.save} />
</div>
)
}
})
var PeopleContainer = React.createClass({
componentWillMount(){
console.log(this.props)
this.setState({
currentChildren: this.props.people[0].children,
parentName: this.props.people[0].name
})
},
onChildChangeName(event,index){
console.log(event.target.value,index);
var newChildrenArray = this.state.currentChildren;
newChildrenArray[index].name = event.target.value
this.setState({
currentChildren: newChildrenArray
})
this.props.saveAction();
},
onParentChangeName(event){
this.setState({
parentName: event.target.value,
})
this.props.saveAction()
},
render(){
return (
<div>
Parent Name : <input value={this.state.parentName} onChange={(event) => this.onParentChangeName(event)} />
<div><br /></div>
{this.state.currentChildren.map((child, index) => {
return(<div key={index}>
Name : <input value={child.name} onChange={(event) => this.onChildChangeName(event,index)} /> <div><br /></div>
</div>)
})}
</div>
)
}
})
// Map state and dispatch to props
function mapStateToProps (state) {
return {
peopleInfo: state[0].peopleInfo,
};
}
function mapDispatchToProps (dispatch) {
return Redux.bindActionCreators({
save: save,
}, dispatch);
}
// Container components (Pass props into presentational component)
var OrgsContainer = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(OrgsContainer);
// Top-Level Component
var App = React.createClass({
render: function() {
return (
<div>
<h3>App</h3>
<OrgsContainer />
</div>
);
}
});
// Render to DOM
var Provider = ReactRedux.Provider; // Injects store into context of all descendents
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('container')
);
所以它无关反应原生。
减速器上的结构模仿我在我的应用程序中使用的数据模型。
从小提琴中可以看出,很显然,我们不能让道具通过设置状态并在那里改变。显然存在由于对象引用而形成的商店的链接。更新这种参考最终会更新商店。
虽然不让你的道具设置在状态是一个好习惯,但我的场景需要它。现在我已经使用了Object.assign()来创建一个新的对象,并在状态中使用这个对象,这对我很有用。
我可能已经错过了关于这个的redux文档中的一些内容。如果任何人碰巧发现了什么,我会很高兴知道这件事。
但我仍然觉得这很奇怪。
最终,你的问题的原因是,在你的onChildChangeName
方法:
var newChildrenArray = this.state.currentChildren;
newChildrenArray[index].name = event.target.value
你是变异相同的底层对象实例,你的店也被引用。
可以通过添加此行onChildChangeName
证实了这一点:
console.log(intialState[0].peopleInfo[0].children === newChildrenArray); // true
解决方案1: 最简单的办法是,你可以在数据创建一个深拷贝,当你第一次状态,例如:
this.setState({
currentChildren: _.cloneDeep(this.props.people[0].children),
parentName: this.props.people[0].name
})
由于这会在阵列上创建深层副本,因此阵列n或者数组中的项目引用与您的商店相同的数据,所以您现在可以安全地更改数组/对象而不用担心副作用。
解决方案2: 另一种选择是首先创建数组的一个浅表副本,然后确保你创建新的对象实例时,你需要在阵列中更改的项目。例如,当第一次调用setState
,通过执行slice()
,以确保更改的组件的排列并不在由initialState
引用的实例发生创建一个新的数组,如:
this.setState({
currentChildren: this.props.people[0].children.slice(),
parentName: this.props.people[0].name
})
然后在onChildChangeName
,你总是创建一个新的实例,而不是突变的现有实例,例如:
var newChildrenArray = this.state.currentChildren;
newChildrenArray[index] = {...newChildrenArray[index], name: event.target.value}
虽然反应,和/终极版都绕过数据的各种情况下为您打造您的组件,它们不执行任何形式的克隆/复制操作确保你使用新的参考因此你必须自己去做,以避免这些问题。
请分享一些代码 – Junior