简体   繁体   中英

<Redux> state updated but nor re-render

I just started leaning React Native + Redux. It seems I successfully updated the state but the view does not change(not re-rendered). Could anyone give me an advice to solve this...?

I tried to debug with using console.log and tap the screen.

"selectedMenuMap": Object { "item2": true, "item3": false, "}, This is just a part of the logs and I could see the state is actually changed...

Reducer.js

 import { SELECT_MENU } from './actions.js'; const initialState = { menu: [ {key: 'item1', title: 'AAA', detail: [ {key: 'detail_item1', title: 'AA', price: '1000', time: '30'}, {key: 'detail_item2', title: 'AB', price: '1100', time: '35'}, {key: 'detail_item3', title: 'AC', price: '1200', time: '40'}, ]}, {key: 'item2', title: 'BBB', detail: [ {key: 'detail_item1', title: 'BA', price: '1000', time: '30'}, {key: 'detail_item2', title: 'BB', price: '1100', time: '35'}, {key: 'detail_item3', title: 'BC', price: '1200', time: '40'}, ]}, {key: 'item3', title: 'CCC', detail: [ {key: 'detail_item1', title: 'CA', price: '1000', time: '30'}, {key: 'detail_item2', title: 'CB', price: '1100', time: '35'}, {key: 'detail_item3', title: 'CC', price: '1200', time: '40'}, ]}, ], selectedMenuMap: {} }; function menuList(state = initialState, action) { switch (action.type) { case SELECT_MENU: console.log('state'); let newState = Object.assign({}, state); newState.selectedMenuMap[action.key] = !state.selectedMenuMap[action.key]; console.log(newState === state); return newState; default: return state; } } export default menuList; 

Container.js

 import MenuList from '../components/MenuList.js'; import { selectMenu } from '../actions.js'; import { connect } from 'react-redux'; const mapStateToProps = state => ({ menu: state.menu, selectedMenuMap: state.selectedMenuMap, }); const mapDispatchToProps = dispatch => { return { onPressMenu: key => { dispatch(selectMenu(key)) }, } } export default connect( mapStateToProps, mapDispatchToProps )(MenuList); 

components.js

 import React, { Component, Purecomponent } from 'react'; import { FlatList, } from 'react-native'; import MenuItem from './MenuItem.js'; export default class MenuList extends React.PureComponent { _renderItem = ({item}) => ( <MenuItem id={item.key} onPressItem={() => this.props.onPressMenu(item.key)} selected={this.props.selectedMenuMap[item.key]} title={item.title} detail={item.detail} /> ); render() {console.log(this.props); return ( <FlatList data={this.props.menu} renderItem={this._renderItem} /> ); } } 

Is this a problem???state and newState are the same??

 function menuList(state = initialState, action) { switch (action.type) { case SELECT_MENU: console.log('reducer'); let newState = Object.assign({}, state); newState.selectedMenuMap[action.key] = !state.selectedMenuMap[action.key]; console.log('oldState'); console.log(state.selectedMenuMap); console.log('newState'); console.log(newState.selectedMenuMap); console.log(newState.selectedMenuMap === state.selectedMenuMap); return newState; default: return state; } } 17:35:33: reducer 17:35:33: oldState 17:35:33: Object { 17:35:33: "item3": true, 17:35:33: } 17:35:33: newState 17:35:33: Object { 17:35:33: "item3": true, 17:35:33: } 17:35:33: true 

Object.assign() only performs a shallow copy. So this statement doesn't work as you expect.

  let newState = Object.assign({}, state);

While newState and state are different objects, the nested newState.selectedMenuMap points to the same object as state.selectedMenuMap . You cannot mutate one without mutating both.

Instead make sure you do not modify the original state. Something like this should work.

const newState = { ...state,
  selectedMenuMap: { ...state.selectedMenuMap,
    [action.key]: !state.selectedMenuMap[action.key]
  }
}

Each spread operation {...foo} does the same as Object.assign({}, foo) , so we create a shallow copy of both state and then of state.selectedMenuMap . Now state.selectedMenuMap and newState.selectedMenuMap are pointing to different objects, and redux will know that components that connects to this part of the state tree should be rerendered.

This looks a bit complicated, and it might not be obvious whether the original state is mutated or not here.

One way to avoid mutating existing state when updating nested state slices is to use a library such as Ramda.js which never mutates the objects passed in as arguments.

const newState = R.over(R.lensPath(['selectedMenuMap', action.key]), R.not, state)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM