Redux & NavigatorIOS does not update state in chil

2019-09-03 03:57发布

问题:

How can I make redux update master and detail views contained inside a NavigatorIOS?

Both the Master and Detail views do not update:

MainView > TabBar > TabBarItem > NavigatorIOS > MasterMenu > Detail

Other TabBarItem's work great using redux. For example:

MainView > TabBar TabBarItem > ViewProfile

  <Icon.TabBarItem
  style={{borderColor:'red', borderWidth:0}}
  {...this.props}
  iconName="timeline"
  title="View"
  selected={this.state.selectedTab === 'viewTab'}
  onPress={() => {
    this.setState({
      selectedTab: 'viewTab',
      presses: this.state.presses + 1
    });
  }}>
  <ViewProfile {...this.props} />
  </Icon.TabBarItem>

This MainView > TabBar > TabBarItem > NavigatorIOS > MasterMenu > Detail does NOT update:

<Icon.TabBarItem
style={{borderColor:'red', borderWidth:0}}
title={'Menu'}
iconName="more-vert"
selected={this.state.selectedTab === 'moreTab'}
onPress={() => {
  this.setState({
    selectedTab: 'moreTab',
  });
}}>
<View style={ss.container}>
<NavigatorIOS
ref="nav"
style={ss.container}
{...this.props}
initialRoute={{
  title: 'Master Menu',
  component: MasterMenu,
  passProps: {
    ...this.props,
  },
}}
/>
</Icon.TabBarItem>

</TabBarIOS>

This is how I connect and pass props to MainView that contains the NaigatorIOS > MasterMenu > Detail

File: mainApp.js

class MainApp extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    console.log('****** MainApp::render ****');
    const { state, actions } = this.props;
    return (
      <MainView
        {...this.props} />
    );
  }
}

// mapStateToProps
function mapStoreStateToComponentProps(state) {
  return {
    state: state.appState,
  };
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Object.assign({}, appActions), dispatch),
  }
}
export default connect(
  mapStoreStateToComponentProps,
  mapDispatchToProps
)(MainApp)

I can drill down into a NavigatorIOS containing a MasterMenu->DetailMenu view, call an action in the detail view, the state changes and all other views in TabBarIOS update. However, the redux state in both the master and detail views contained inside the <NavigatorIOS> retain their original values.

Is passProps not the way to do it?

Or is there other listen or subscribe methods that a master and child view can use to update when data store state changes?

回答1:

Known issue with NavigatorIOS

NavigatorIOS does not rerender a scene, when passProps changed.

Bypass by connecting redux at a lower point in the hierarchy

You could bypass this issue by connecting your component MasterMenu with redux, instead of MainView.



回答2:

The advice of @purii solved my problem.

I created a wrapper for the "Master View" component loaded into NavigatorIOS component.

I also created wrappers for all "Child View" components that were drilled-down into from the "Master View" component. This seems ugly and wrong but it works. Using NavigatorIOS passProps seemed to break redux updates.

var React = require('react-native');
var {
  Component,
} = React;


import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';

import * as appActions from '../actions/appActions';

var MasterListView = require('../../MasterListView');

class MasterListWrapper extends Component {
  constructor(props) {
    super(props);
  }
  render() {
    console.log('****** PlanList::render ****');
    return (
      <MasterListView {...this.props} />
    );
  }
}

// mapStoreStateToComponentProps would be a more descriptive name for mapStateToProps
function mapStateToProps(state) {
  return {
    state: state.appState,
  };
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Object.assign({}, appActions), dispatch),
  }
}
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MasterListWrapper)

And then I pass MasterListWrapper as the NavigatorIOS component:

<Icon.TabBarItem
style={{borderColor:'red', borderWidth:0}}
title="Plan"
iconName="list"
selected={this.state.selectedTab === 'planTab'}
onPress={() => {
  this.setState({
    selectedTab: 'planTab',
    presses: this.state.presses + 1
  });
}}>
<View style={ss.container}>
<NavigatorIOS
ref="nav"
style={ss.container}
initialRoute={{
  title: 'Field Planner',
  component: MasterListWrapper,
  passProps: {
  },
}}
/>
</View>

</Icon.TabBarItem>

In the MasterListView I needed to add componentWillReceiveProps to get my ListView to re-render when redux state changed.

  componentWillReceiveProps: function(nextProps) {
    this.setState({
      // Force all rows to render
      dataSource: ds.cloneWithRows(nextProps.state.waypointItemList),
    });
  },

This is likely not the best approach. I will be looking for better approaches.

However at this point in my learning curve of javascript and react-native, I am happy it worked.

Redux is pretty cool. I have multiple tabs. I can go to menu and change a settings, for example, change units from feet to meters, and all my views are re-rendered.