2016-12-24 55 views
0

我已經在多個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文檔中的一些內容。如果任何人碰巧發現了什麼,我會很高興知道這件事。

但我仍然覺得這很奇怪。

+1

請分享一些代碼 – Junior

回答

1

最終,你的問題的原因是,在你的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} 

雖然反應,和/終極版都繞過數據的各種情況下爲您打造您的組件,它們不執行任何形式的克隆/複製操作確保你使用新的參考因此你必須自己去做,以避免這些問題。