首页 文章

在组件更新redux存储中React Native setState

提问于
浏览
3

我在多个Web应用程序中使用了redux with react . 目前我正在使用Redux开发我的第一个React Native应用程序 .

我遇到了一个非常奇怪的问题 .

我创建了一个商店并将其传递给Provider组件,该组件将App组件呈现为子组件 . (react-redux的基本用法)

在应用程序中,我有一个连接组件 . 它在调用navigator.push(route)时将从商店收到的数据传递给Navigator Route . 此路由中的组件不是连接组件 . 它接收道具和 stores the props in its state. 道具不仅仅是文字,但它们是对象/数组 . 根据用户交互,此组件通过setState更新其状态 . This operation is directly updating the store .

我不应该通过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>
        
</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>
</div> {this.state.currentChildren.map((child, index) => { return(<div key={index}> Name : <input value={child.name} onChange={(event) => this.onChildChangeName(event,index)} /> <div>
</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') );

所以它与React Native无关 .

reducer上的结构模仿我在我的应用程序中的数据模型 .

从小提琴中可以看出,很明显我们不能将道具传递给国家并在那里改变 . 很明显,存在由于对象引用而形成的商店的链接 . 更新此类引用最终会更新商店 .

虽然不把你的道具设置在状态上是一个好习惯,但我的场景需要它 . 现在我使用了Object.assign()来创建一个新对象并在状态上使用这个对象,这对我有用 .

我可能在redux文档中遗漏了一些信息 . 如果有人碰巧找到了什么,我很乐意知道 .

但我仍然觉得这很奇怪 .

1 回答

  • 3

    最终,问题的原因是,在您的 onChildChangeName 方法中:

    var newChildrenArray = this.state.currentChildren;
    newChildrenArray[index].name = event.target.value
    

    您正在改变商店也引用的相同底层对象实例 .

    您可以通过将此行添加到 onChildChangeName 来确认:

    console.log(intialState[0].peopleInfo[0].children === newChildrenArray); // true
    

    Solution 1: 最简单的解决方案是,在第一次设置状态时可以在数据上创建深层副本,例如:

    this.setState({
      currentChildren: _.cloneDeep(this.props.people[0].children),
      parentName: this.props.people[0].name
    })
    

    由于这会在数组上创建深层副本,因此数组和数组中的项都不会引用与 initialState (您的商店)相同的数据,因此您现在可以安全地更改数组/对象而不必担心副作用 .


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

    虽然React / Redux在构建组件时传递各种数据实例,但它们不执行任何类型的克隆/复制操作以确保您使用新引用,因此您必须自己执行这些操作以避免这些问题 .

相关问题