Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Navigation: How to update navigation title for parent, when value comes from redux and gets updated in child?

I'm using react-navigation and have a StackNavigator with a ParentScreen and a ChildScreen.

Both screens have the same navigation bar with a dynamic value from redux. Implemented like described in Issue #313

This works as expected. When I'm in DetailScreen and I update the value for the count variable, it also updates the value in the navigation bar.

Problem is, if I go back to the parent scene, there is still the old value in the navigation bar. It doesn't update to the current value in redux store.

Child

screen shot 2017-04-11 at 15 20 28

Parent (when I go back)

screen shot 2017-04-11 at 15 21 31

ChildScreen

class ChildScreen extends Component {
  static navigationOptions = {
    title: ({ state }) => `Total: ${state.params && state.params.count ?  state.params.count : ''}`
  };

  componentWillReceiveProps(nextProps) {
    if (nextProps.count != this.props.count) {
      this.props.navigation.setParams({ count: nextProps.count });
    }
  }

  render() {
    return (
      <View>
        <Button onPress={() => this.props.increment()} title="Increment" />
      </View>
    );
  }
}

ParentScreen

class ParentScreen extends Component {
  static navigationOptions = {
  title: ({ state }) => `Total: ${state.params && state.params.count ?    state.params.count : ''}`
  };
}

Any advice?

like image 773
crispychicken Avatar asked Apr 11 '17 13:04

crispychicken


2 Answers

My advice:

  1. Make sure that ParentScreen is connected through react-redux connect function.

  2. If you want the title of ParentScreen to get updated automatically when the state of the store changes, connecting it won't be enough. You will have to use the componentWillReceiveProps like you are doing in the ChildScreen Component.

Bonus: you could create a Higher Order Component for encapsulating that behaviour, like this:

const withTotalTitle = Component => props => {
  class TotalTitle extends Component {
    static navigationOptions = {
      title: ({ state }) => `Total: ${state.params && state.params.count ?  state.params.count : ''}`
    };

    componentWillReceiveProps(nextProps) {
      if (nextProps.count != this.props.count) {
        this.props.navigation.setParams({ count: nextProps.count });
      }
    }

    render() {
      return (
        <Component {...props}/>
      );
    }
  }

  return connect(state => { count: state.total })(TotalTitle); // update this (I have no idea what the structure your state looks like)
};

And then you could use it like this:

const ChildScreen = withTotalTitle(({ increment }) => (
  <View>
    <Button onPress={() => increment()} title="Increment" />
  </View>
));

const ParentScreen = withTotalTitle(() => (
  <View>
    <Text>Whatever ParentScreen is supposed to render.</Text>
  </View>
));
like image 68
Josep Avatar answered Sep 22 '22 19:09

Josep


Have a common reducer for both parent and child. That way all the components(parent and child in your case) will get notified when the state changes.

Write a connect function for both parent and child. You'll receive the updated state in componentWillReceiveProps method. Use it as you please.

Hope it will help.

like image 38
atitpatel Avatar answered Sep 20 '22 19:09

atitpatel