New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When async-loaded state gets passed via screenProps it doesn't update Navigators #577

Closed
mostr opened this Issue Mar 5, 2017 · 6 comments

Comments

Projects
None yet
8 participants
@mostr
Copy link

mostr commented Mar 5, 2017

I'm trying to pass initial, async-loaded application data down to presentation components and I'd like to do it with vanilla-react approach (with plain old setState, without redux and other external state management techniques.

I wanted to use screenProps for that, to load initial state in top level component, and rerender the <Nav screenProps={this.state} but got into really weird case.

First of all question: whether screenProps are a good place to thread initial data through nested components when loaded?

Now on to the issue I see. My App component looks like this:

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = {};
  }

  componentWillMount() {
    loadData().then(data => this.setState({data}))
  }

  render() {
    return (
      <View style={styles.container}>
        <Nav screenProps={this.state}/>
      </View>
    );
  }
}

Components deep in Nav display this screenProps stuff.

But in this case initial loading does work fine (deep components display Loading as expected) but when data gets loaded it seems that Nav is not rerendered (components don't see screenProps change).

Only when I change render() and add something more to display there, it works fine and components deep in Nav get new screenProps :

  render() {
    return (
      <View style={styles.container}>
        {this.state.data && <Text>foo bar</Text>}
        <Nav screenProps={this.state}/>
      </View>
    );
  }

Is this expected and I'm missing something obvious, or is this a bug?
What are other ways to populate nested components with initial async-loaded state without redux and such?

@kimdhoe

This comment has been minimized.

Copy link

kimdhoe commented Mar 12, 2017

I'm not sure this is a bug. React's setState does not mutate each items in state, but it does mutate state object itself. Some internal PureComponent will always see screenProps as the same object.

@taiki-t

This comment has been minimized.

Copy link
Contributor

taiki-t commented Mar 23, 2017

SceneView extends PureComponent and

React.PureComponent's shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences.

https://facebook.github.io/react/docs/react-api.html#react.purecomponent

hmm...

@doomsower

This comment has been minimized.

Copy link

doomsower commented Mar 29, 2017

The culprit here is react-native-tab-view. See caveats section. The workaround is to stuff screenProps into navigation.state as well. Here is my example, withRegion is graphql container.

const RegionTabs = TabNavigator(
  {
    Map: { screen: RegionMapScreen },
    Description: { screen: RegionDescriptionScreen },
    Sections: { screen: SectionsListScreen },
  },
  {
    initialRouteName: 'Map',
    tabBarPosition: 'bottom',
  },
);

export default compose(
  setStatic('router', RegionTabs.router),
  withRegion({ withBounds: true }),
  mapProps(({ region, regionLoading, navigation, ...props }) => {
    const screenProps = { region, regionLoading };
    return {
      ...props,
      navigation: { ...navigation, state: { ...navigation.state, screenProps } },
      screenProps,
    };
  }),
)(RegionTabs);

This is quite ugly, but seems that this is how react-navigation and react-native-tab-view are designed. They want you to place you data containers at screen level, this works fine with redux and selectors, but I don't know how to make this work well with apollo.

@stochris

This comment has been minimized.

Copy link
Contributor

stochris commented Mar 30, 2017

I suggest sending a new copy of state on every render and the navigators should be updating :

  const stateClone = Object.assign({}, this.state);
  <Nav screenProps={stateClone} />

In case you're using a TabNavigator, it's not gonna update when screenProps update. If you use it, switch to the master branch, and it should update.

@matthamil

This comment has been minimized.

Copy link
Member

matthamil commented Aug 3, 2017

Hi there! This issue is being closed because it has been inactive for a while. We're closing issues after a period of inactivity. Please do not take this personally :)
If you think this issue should remain open, please let us know. The following information is helpful when it comes to determining if the issue should be re-opened:

  • Does the issue still reproduce on the latest release? Post a comment with the version you tested.
  • If so, is there any information missing from the bug report? Post a comment with all the information required by the issue template.
  • Is there a pull request that addresses this issue? Post a comment with the PR number so we can follow up.
    If you would like to work on a patch to fix the issue, contributions are very welcome! Read through the contributors guide, and feel free to hop into #react-navigation if you need help planning your contribution.

@matthamil matthamil closed this Aug 3, 2017

@PragyaTripathi

This comment has been minimized.

Copy link

PragyaTripathi commented Aug 20, 2018

I faced the exact same problem as you, @mostr. Resolved it by dispatching params once I had them available. This worked beautifully for me. Courtesy of perusing the docs: https://reactnavigation.org/docs/en/navigation-actions.html#setparams It even guarantees to change the state.

const setParamsActionLeaderboard = NavigationActions.setParams({
					params: { contract: this.props.contract, accountHash: accountHash },
					key: 'Leaderboard',
				});
this.props.navigation.dispatch(setParamsActionLeaderboard);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment