[RFC] Declarative navigator routeStack API #2436

Closed
tgriesser opened this Issue Aug 25, 2015 · 10 comments

Comments

Projects
None yet
7 participants
@tgriesser

The Navigator component stores quite a bit of information internally in this.state, which makes sense given the complexity of scene transitions. However, the imperative APIs needed to work with the Navigator component (pop, push, replaceAtIndex, etc.) are unfamiliar when coming from other traditionally declarative React core components.

I'd like to propose adding a new (optional) declarative API:

<Navigator routeStack={routes} />

The routeStack prop would be an Array acting as both the initialRouteStack during getInitialState as well as diff'ed against the current state.routeStack in componentWillReceiveProps. When the routeStack entries differ, the component would make its best guess at how to transition, mount/unmount scenes, etc. This would allow for more complex manipulation of a scene route stack outside of the component, while also simplifying the API surface area of the Navigator.

If this seems like a reasonable proposal, I'd be happy to take a shot at implementing - haven't explored enough to see if there are any blockers that would prevent this from being possible, I'd imagine there are some complexities with gestures, etc.

I searched a bit for other issues and didn't happen upon any related tickets, but let me know if I missed one!

@brentvatne brentvatne changed the title from Proposal: Declarative navigator routeStack API to [RFC] Declarative navigator routeStack API Aug 30, 2015

@brentvatne

This comment has been minimized.

Show comment
Hide comment
@brentvatne

brentvatne Aug 30, 2015

Collaborator

This is an interesting idea... I think that having some way to store the stack outside of the Navigator would extremely useful -- I'd like to be able to store it within a Redux store, for example, so I can save my entire app state in Redux and reload from there. I'd be up for testing out and giving feedback on your implementation if you'd like to move forward with it.

cc @hedgerwang @ericvicenti @ide

Collaborator

brentvatne commented Aug 30, 2015

This is an interesting idea... I think that having some way to store the stack outside of the Navigator would extremely useful -- I'd like to be able to store it within a Redux store, for example, so I can save my entire app state in Redux and reload from there. I'd be up for testing out and giving feedback on your implementation if you'd like to move forward with it.

cc @hedgerwang @ericvicenti @ide

@tgriesser

This comment has been minimized.

Show comment
Hide comment
@tgriesser

tgriesser Aug 30, 2015

Yep, serializing with redux is precisely what I'm doing now and it's really convenient but extremely awkward with current apis.

I'll take a shot at it and see where I get.

Yep, serializing with redux is precisely what I'm doing now and it's really convenient but extremely awkward with current apis.

I'll take a shot at it and see where I get.

@ide

This comment has been minimized.

Show comment
Hide comment
@ide

ide Aug 30, 2015

Collaborator

I believe a declarative API is in the works (or is at least being considered, even if the next version of Navigator doesn't end up declarative). Regarding route serialization you may want to consider using URLs and react-router, which would then make it possible to deeplink into parts of your app. Currently there isn't a good story around this in RN land and we need more people exploring how to do this.

Collaborator

ide commented Aug 30, 2015

I believe a declarative API is in the works (or is at least being considered, even if the next version of Navigator doesn't end up declarative). Regarding route serialization you may want to consider using URLs and react-router, which would then make it possible to deeplink into parts of your app. Currently there isn't a good story around this in RN land and we need more people exploring how to do this.

@brentvatne

This comment has been minimized.

Show comment
Hide comment
@brentvatne

brentvatne Aug 31, 2015

Collaborator

Awesome @tgriesser!

Collaborator

brentvatne commented Aug 31, 2015

Awesome @tgriesser!

@hedgerwang

This comment has been minimized.

Show comment
Hide comment
@hedgerwang

hedgerwang Aug 31, 2015

Contributor

@tgriesser

Internally, we have built the low-level component that renders a stack of route reactively.
For now, the code look like this:


class Playground extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      stack: new NavigationRouteStack(0, [0]),
    };

    this.pop = this.pop.bind(this);
    this.push = this.push.bind(this);
  }

  render() {
    return (
      <View style={styles.main}>
        <NavigationUICardStack
          renderScene={renderScene}
          stack={this.state.stack}
          style={styles.stackView}
        />
        <Button onPress={this.pop} text="Pop" />
        <Button onPress={this.push} text="Push" />
      </View>
    );
  }

  push() {
    this.setState({
      stack: this.state.stack.push(this.state.stack.index + 1),
    });
  }

  pop() {
    if (this.state.stack.size > 1) {
      this.setState({
        stack: this.state.stack.pop(),
      });
    }
  }
}
Contributor

hedgerwang commented Aug 31, 2015

@tgriesser

Internally, we have built the low-level component that renders a stack of route reactively.
For now, the code look like this:


class Playground extends React.Component {
  constructor(props, context) {
    super(props, context);

    this.state = {
      stack: new NavigationRouteStack(0, [0]),
    };

    this.pop = this.pop.bind(this);
    this.push = this.push.bind(this);
  }

  render() {
    return (
      <View style={styles.main}>
        <NavigationUICardStack
          renderScene={renderScene}
          stack={this.state.stack}
          style={styles.stackView}
        />
        <Button onPress={this.pop} text="Pop" />
        <Button onPress={this.push} text="Push" />
      </View>
    );
  }

  push() {
    this.setState({
      stack: this.state.stack.push(this.state.stack.index + 1),
    });
  }

  pop() {
    if (this.state.stack.size > 1) {
      this.setState({
        stack: this.state.stack.pop(),
      });
    }
  }
}
@bleonard

This comment has been minimized.

Show comment
Hide comment
@bleonard

bleonard Aug 31, 2015

Contributor

I've been using the URL as a declarative way. More or less every piece of the path (between slashes) is a component on the stack.

I found it very a nice pattern to never actually push or pop to the navigator, but rather always set the whole stack via the URL.

Then in componentDidUpdate, I check to see how the stack being pushed is different than the current one. If it's completely different, I just immediatelyResetRouteStack, but if it's one added or removed from the current one, I used push/pop.

This space is all still very early, so I've been meaning to put up a sample app about the patterns that we've been using. Let me know if you're interested in hearing more about this routing technique.

Contributor

bleonard commented Aug 31, 2015

I've been using the URL as a declarative way. More or less every piece of the path (between slashes) is a component on the stack.

I found it very a nice pattern to never actually push or pop to the navigator, but rather always set the whole stack via the URL.

Then in componentDidUpdate, I check to see how the stack being pushed is different than the current one. If it's completely different, I just immediatelyResetRouteStack, but if it's one added or removed from the current one, I used push/pop.

This space is all still very early, so I've been meaning to put up a sample app about the patterns that we've been using. Let me know if you're interested in hearing more about this routing technique.

@danscan

This comment has been minimized.

Show comment
Hide comment
@danscan

danscan Sep 10, 2015

Contributor

@bleonard I'd love to see a sample implementation. Working on something similar to this right now in my RN app with declarative routing.

Contributor

danscan commented Sep 10, 2015

@bleonard I'd love to see a sample implementation. Working on something similar to this right now in my RN app with declarative routing.

@bleonard

This comment has been minimized.

Show comment
Hide comment
@bleonard

bleonard Sep 21, 2015

Contributor

Hey everyone, I just published a blog post and sample app with our URL-driven routing.
http://tech.taskrabbit.com/blog/2015/09/21/react-native-example-app/

Hope it is helpful.

Contributor

bleonard commented Sep 21, 2015

Hey everyone, I just published a blog post and sample app with our URL-driven routing.
http://tech.taskrabbit.com/blog/2015/09/21/react-native-example-app/

Hope it is helpful.

@danscan

This comment has been minimized.

Show comment
Hide comment
@danscan

danscan Sep 21, 2015

Contributor

@bleonard– this is a great post. I'm doing something similar now for declarative navigation with multiple route histories, as our app's primary nav is a tab bar, and each tab has its own history.

Contributor

danscan commented Sep 21, 2015

@bleonard– this is a great post. I'm doing something similar now for declarative navigation with multiple route histories, as our app's primary nav is a tab bar, and each tab has its own history.

@ide

This comment has been minimized.

Show comment
Hide comment
@ide

ide Jan 9, 2016

Collaborator

@ericvicenti put up an experimental declarative Navigator here: https://github.com/ericvicenti/navigation-rfc. Closing this issue here so that discussion moves to that repo.

Collaborator

ide commented Jan 9, 2016

@ericvicenti put up an experimental declarative Navigator here: https://github.com/ericvicenti/navigation-rfc. Closing this issue here so that discussion moves to that repo.

@ide ide closed this Jan 9, 2016

@facebook facebook locked as resolved and limited conversation to collaborators Jul 21, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.