Just tracking react native thoughts here. My assumptions are probably all wrong since 1) I don't know much about native dev and 2) I've spent 5 minutes with react native but:
Location
and eventually an Android
location, it'll probably be stored in memory and persisted to some sort of localStorage
equivalent. When the app boots we look at the URI requesting the launch first, and then the localStorage
(whatever it really is) second.Again, I have no idea what I'm talking about yet. ¯(º_o)/¯
There will be a localStorage
with the same API as the regular one except async
Possibly of interest: http://applinks.org/
I'd prefer to have a synchronous way to store URLs, just because all our transitions are sync by default.
@rpflorence Ideally we would be able to render the top-level component and not have to embed the router in some other component, no?
I'm just making wild guesses, but every app has Bundler.registerComponent('MoviesApp', () => MoviesApp);
which appears to be rendered automatically.
The components would have to be different too. You don't have a DOM <a>
, so <Link>
needs to reimplement it.
Strictly I wouldn't say <Link>
is beneficial in React Native. I rarely see iOS interfaces using links.
Most of the times I assume you'd just use transitionTo
and friends.
Perhaps, though, links are _the_ navigation paradigm on the web (not just for text). As part of "Learn Once, Write Everywhere," there's a strong case to be made for maintaining a declarative way to create transitions (even if they don't default to blue text).
For instance, I think this should work with react-native:
<Link to = "home">
<Image source = { ix("logo.svg") } />
</Link>
Yes, I suppose you're right.
I wonder how having different targets (like Web and React Native) would work for a library.
The naive solution would be something like this:
try {
var React = require("react/addons");
} catch (error) {
if (!error.message.includes("Cannot find"))
throw error;
var React = require("react-native/addons");
}
and then choose a "div"
or a View
depending on what's available. It would be nice if they made this easier in the standard library though.
I came really close to submitting a feature request to have DOM shims for the RN primitives (View
, Text
, and Image
) added to React, but I backed down when I realized that's probably too close to "write once, run everywhere". For instance, img
uses srcSet
and Image
uses source
. I imagine reimplementing srcSet
on top of Image
is exactly the kind of maintenance hell they are trying to avoid by discouraging component reuse across platforms.
Perhaps branch via process.env
in some way? Webpack and browserify users could remove dead code, and library authors could offer pre-bundled versions for each target (require('library/android')
). Default could be web, or determining target at runtime.
@appsforartists You're right, the goal of React Native is not to enable "write once" mentality, thus I think your example would be an anti-pattern. You should maintain different code bases for different platforms. But I do agree with you that Links should be available much like they are on the web. Views should be linkable. Otherwise I'd expect that it would be possible to route using regular JS from a click handler. (Not sure if this is already possible with React router - I'm assuming it is.)
I know there hasn't been any major progress here yet, but I think it would be nice, if the router would be "smart" about where to transition to. If we have a navigation stack which already contains a
, b
and c
, and we then want to transition to a
, it would be nice if the router then popped b
and c
from the stack in order to get to a
, instead of adding a new instance of a
to the stack. This type of "infinite pushing" is a common mistake/error I have seen in iOS applications and if we could tackle that on the router level, that would be pretty nice I think.
edit: Since there actually is already a Navigator
component available, which is being used to navigate between scenes and to determine which scene to render initially etc., the router could probably somehow leverage that under the hood?
we're working on this, don't think we need the issue anymore.
Any update on this? React Router on RN would be HUGE.
+1
ping @ryanflorence - curious to hear how this is coming along and if there is anything that we can do to help! I see the most recent commit on the react-native branch was about 21 days ago: https://github.com/rackt/react-router/commit/bf6f9a723f94b967f56196a9a711518ecd0b8e01 - if you could put up a checklist for remaining tasks perhaps a few of us could try to tackle them
I too would like to know. I see there's a branch here: https://github.com/rackt/react-router/tree/react-native
Do you guys have an estimate for the completion of this? Would you like help?
Anybody...? It looks like the react-native
branch has been deleted. I know support for react-native was being developed for history
, but would love an update on where things stand for react-router
.
+1
+1
Where can I find a sample of react-router + react-native?
:+1:
+1
+1
:+1:
@ryanflorence Any update on this? Just about to start a big project in RN and would love to use React Router.
This isn't shippable in a reasonable way without the RN/React merge without something like https://github.com/rackt/react-router/pull/2031. And this issue is asking the wrong question anyway - the question is integration with Navigator, not some abstract support on its own.
cc @skevy
Yah just a report on what I've been up to -
I have a working prototype of this in an app that's currently in beta at the moment. It's rough around the edges, but I think there's definitely going to be a way to make it work in a stable manner. Some of the harder parts around the integration is not actually developing _a_ solution, but really figuring out the API around the _right_ solution. Routing on the web does not map directly to routing on native. @ide talks about it a bit here: https://medium.com/the-exponent-log/routing-and-navigation-in-react-native-6b27bee39603.
The main issue is that a url of /app/me/profile
doesn't give any information about the routes that came before it, which is something you need in a mobile app -- a concept of "implied" routes. In addition, most mobile apps have multiple navigators (each tab in a tabbed application usually has a nested navigator)...so the problem of how to deal with hierarchal history is an issue. There are multiple ways to solve the problem, but that's kind of the state of the world (as I'm aware) at the moment.
getChildRoutes + location.state I've imagined being the levers to get "implied" routes.
But also, ignoring that use case, I've imagined we don't try to make react router understand iOS routing semantics, we bring web routing semantics to iOS.
@ryanflorence I agree with the second comment - that the goal of using RR on RN would be to allow the developer to not spend a lot of time thinking about native navigation, and instead allow them to focus on reaping the benefits of RR - fast screen creation, making the URL your first thought, etc.
Nevertheless, we're not going to change how mobile apps _actually_ work. Thanks to Apple, people expect a certain experience when they use an app - it's our job as developers to provide that. So, from an implementation perspective, we do have to care about iOS/Android routing semantics - it's just not necessarily what the public API represents.
@ryanflorence I agree with your last comment. iOS doesn't really have strong opinions about routing anyway.
One feature I would like to see in a routing system (however it ends up looking) is that URLs are handled top-to-bottom if there is some kind of route or router hierarchy, as opposed to bubbling them upwards. The conceptual equivalent of window.location = X would work in that world. This opens the door to building a history of URLs and making it easy to move within that history (ex: building back and forward functionality).
@ryanflorence and regarding getChildRoutes
+ location.state
-- yes, that's approximately what I'm doing.
The real thing I've struggled with, personally, is with managing history. Right now, I basically am doing the equivalent of replaceState
when I want to navigate somewhere...I don't keep a history in the traditional way. This is gross really, and I'd like the user to be able to use normal semantics in rackt/history like push, pop, etc. I think something like what @ide is proposing is the right direction...but haven't had a lot of time as of late to sit with actually implementing.
But I will say, that no matter what the final APIs end up looking like for RR+RN - being able to do things like this:
const routes = (
<NavigatorRoute id="me" path="me" tabItem={{
title: 'Me',
icon: require('image!icon-me')
}}>
<IndexRoute onEnter={(location, replaceWith) => { replaceWith(null, '/app/me/profile'); }}/>
{/** Profile **/}
<RelayRoute path="profile" component={Main} />
<RelayRoute path="settings" component={Settings} />
{/** Payment **/}
<RelayRoute path="payment-details" component={PaymentDetails} />
<RelayRoute path="add-payment-method" component={AddPaymentMethod} />
{/** History **/}
<RelayRoute path="history" component={ReceiptHistory} />
<RelayRoute path="history-summary/:ticketId/" component={ReceiptSummary} />
</NavigatorRoute>
);
on mobile is life changing.
You don't nest your Relay routes? I thought that was something of your motivation in starting down this rabbit hole.
haha @taion I do - this is just one level. I have routes above this that are relay routes.
@ryanflorence I'm going to re-open this issue for now because I feel like we have a bit to discuss here; just another thing to postpone for post-1.0.0 though.
Regarding
In addition, most mobile apps have multiple navigators (each tab in a tabbed application usually has a nested navigator)...so the problem of how to deal with hierarchal history is an issue.
Seems like this maps to a top-level "meta-router" that swaps in and out "child router" objects with separate history
objects, each of which manages its own history stack, no? Do these hierarchies need to go arbitrarily deep, or is one parent/child level sufficient? Multiple nested tab bars would be... odd...
The meta-router thing is something I've thought about, but not yet explored.
A very common pattern for an app (as far as navigator hierarchy is concerned) is this:
- App level Navigator (to go between things like onboarding and the main app)
---> Tab bar
---> Navigator 1
---> Navigator 2
---> Navigator 3
---> Navigator 4
---> Navigator 5
---> Some arbitrary modal
---> Navigator inside Modal
What do you mean "to go between things like onboarding and the main app"? You can't go back to onboarding scenes once you hit the main app, can you?
You could go back to the sign in screen when you sign out.
Note - this is just one way. You could also structure it where the onboarding is in basically a modal-type view, that then goes away when you've gone through it.
Though, in RN, it's recommended to use Navigator to do modal views in a fully React Native app, rather than using the <Modal>
component (which is recommended for hybrid apps).
Point is, it really depends. But we should be able to support this use case in one way or another.
Ah, okay, didn't understand that Modal/Navigator thing.
So the idea would be to have something like what e.g. ExNavigator
has - i.e. associate a createNavigatorHistory()
object and a routing context with each NavigatorRoute
in your above example.
Then to show a modal, do something like (stealing the syntax from #2186)
this.context.history.parent.push({
pathname: '/my-modal',
state: whatever
});
or whatever?
Yah that could work, though it would be great if the user didn't have to care whether or not they were pushing to the parent navigator or not.
It would be awesome if when you did this.context.history.push
-- it would just go to wherever it needed in the app, no matter where you are (this would be the whole "bringing web semantics to native" thing).
But then this.context.history.goBack
is more difficult...because you don't _usually_ want to go back to some arbitrary route in your application (though I can see times when you would want to do that)...you usually want to pop the most recent view off the current navigator. Maybe it's in this case where the user of the API _does_ have to concern themselves with their current nesting...and whether or not they want to "go back" in the context of the app, or "go back" in the context of their current nested Navigator
.
Navigator
in React Native is a JS component anyway (we don't get native perf), so I think we're ok to not use that if it's non-trivial. I think the big thing would be the ability to use Animated
to customize how the router transitions. That way we can emulate the style in each iOS and Android, or customize it.
@skevy
Got it - so essentially, expose something like a hierarchy of history
objects for go(n)
, but not for push
/replace
because it's "obvious" in some sense which history object should receive the new history entry.
Regardless I think the main point is that a lot of the complexity here would be in setting up a createNavigatorHistory
- then the routing piece itself should more or less fall out naturally.
If we completely lost all the code in this repo tomorrow, we'd still be able to build a workable if primitive router without too much effort off of history
. Going the other way around would be tougher.
@taion agreed - the nested history (I think) is crucial here.
@lwansbrough I disagree that we wouldn't want to use Navigator
inside of whatever this implementation ended up looking like. It does get relatively good perf, and will only continue to get better as Facebook dedicates more time to it -- I feel like we would want those benefits and wouldn't to reimplement from scratch. Doing it _with_ Navigator is definitely doable...as I said, I already have a working version of this, but like @taion mentioned, it's really not the Navigator/TabBar/whatever integration that's non-trivial...it's deciding how to deal with History and how to maintain the state.
I guess most importantly, I'm unconvinced that "rolling our own" Navigator, in one way or another, would result in better performance. The performance problems with Navigator as it exists today arguably are not related to strictly Navigator -- they're underlying React Native problems (that could be solved with things like off-thread animation and incremental rendering) that should be fixed in RN core. In addition, I'm pretty sure there's a WIP implementation of Navigator
with Animated
being used under the hood at FB (not yet released).
Just to take this a little farther, is the core routing abstraction w/nested histories that we want to figure out something like... assuming the routing hierarchy looks like
<NavigatorRoute path="/">
<Route path="tabs" component={Tabs}>
<NavigatorRoute path="tab1" component={Tab1}>
<IndexRoute component={Tab1Index} />
<Route path="scene2" component={Tab1Scene2} />
</NavigatorRoute>
<Route path="tab2" component={Tab1} />
</Route>
<NavigatorRoute path="modal" component={Modal}>
<IndexRoute component={ModalIndex} />
<Route path="scene2" component={ModalScene2} />
<Route path="scene3" component={ModalScene3} />
</NavigatorRoute>
</NavigatorRoute>
Some interesting examples of transitions are e.g.
| From | Action | To | Notes |
| --- | --- | --- | --- |
| /tabs/tab1/scene2
| PUSH
| /modal
| PUSH
onto the history for /
to activate the modal, then activate the history for /modal
|
| /modal
| PUSH
| /modal/scene2
| PUSH
onto the history for /modal
|
| /modal/scene2
| PUSH
| /modal/scene3
| PUSH
onto the history for /modal
|
| /modal/scene3
| POP
| /modal/scene2
| POP
from the history for /modal
|
| /modal/scene2
| POP
| /tabs/tab1/scene2
| POP
from the history for /
|
@taion this looks right to me (from a routing abstraction perspective).
You might want to add to the list of "interesting examples of transitions though". For instance:
| From | Action | To | Notes |
| --- | --- | --- | --- |
| /tabs/tab1/scene2
| PUSH
| /tabs/tab2/scene3
| There is now an implied routing stack possibility here...that the history for "tab2" should contain /tabs/tab2/scene1
and /tabs/tab2/scene2
. And then when the user wants to pop back to scene2
by hitting the back button, they should be able to do so. |
In that example, you wouldn't want the back button to go back to /tabs/tab1/scene2
...it wouldn't be what the user would expect.
cc @ide
Also, another edge case:
User is at /tabs/tab1/scene2
, taps on the "Tab2" tab item, and goes to /tabs/tab2/scene1
. Is this a PUSH
onto the app's history stack? I say "perhaps".
Then, if user goes from /tabs/tab2/scene1
to /tabs/tab2/scene2
, and then taps on the "Tab1" tab item in the tab bar, we'd want to make sure that the user goes back to /tabs/tab1/scene2
(normal iOS behavior).
Not sure if it out of the scope of this issue but is it worth considering passing of data between routes?
@joshhornby You'd just do that with state
or whatever on the location
. It's already supported by history/RR.
@skevy Modelling https://github.com/rackt/react-router/issues/743#issuecomment-154761238 seems like saying that the stack for /tabs/tab2
is now empty, so goBack
on the parent history for /
instead... in which case it might reduce to https://github.com/rackt/react-router/issues/743#issuecomment-154761432, actually, depending on how you model the history entry for /tabs/tab1
on the navigator for /
.
Thought checking this out would be nice.
It has more declarative style for routes and animations than built-in RN navigator stuff, and it has nested routes, aka nested navigators?
I've implemented a routing system into my app. So far it doesn't have support for fancy URIs, but I think it's a good start. Basically, what I've come up with is what amounts to fall through routing.
For my implementation I created a couple new components. I'm using Redux to manage routing state.
I've created a Navigator
which uses the React Native version, but also listens for changes to the route
state, which is a "URI" of sorts. The Navigator
has a routes
prop passed to it which maps all the URIs it handles to route objects which specify the component to load and any other information that the Navigator
needs to open that route. Subsequent routing changes from within the new route page happen one of two ways: at the root Navigator
, or at another navigation component, which could be another embedded Navigator
or (in my case) a custom TabBar
which also listens to the route state in the same way the Navigator
does. If the Navigator
is passed a new route state which it does not control (it checks if the route
is in its routes
prop), it does nothing. It is then assumed that some other component below it will be handling the route.
So each component that is interested in the routing state listens for the changes itself, and determines if it needs to change based on the routes it controls. I haven't done much consideration with the back/forward functionality yet. I'm thinking the Navigator
/TabBar
of the component that owns the button that was pressed would "go back". For something like React Router, I guess this would mean that the routing history would have to be kept separately based on the component that handled the route, and the back/forward triggered within that component would act on the history of that component. I think this would work for most common scenarios.
Thoughts?
Edit: I should mention with regards to transitions - I think the type of transition should have some sane default (swipe in from right to left, for example) and it should be up to the developer to indicate which transition they want in their <Link>
. I've actually called this routingBehaviour
in my implementation, because it actually needs to control how new pages are added to the routing stack too. So for me routingBehaviour = "push"
would do a right to left swipe and push the page onto the routing stack. This may require more fine grained control in the future though, in the case where I want to push but animate left to right.
@lwansbrough
That sounds quite cool!
One of the things I care about preserving is the sort of router/history separation, though. I feel like it adds a lot of clarity and structure to the codebase for React Router, and makes it very easy to reason about what goes where.
In terms of transition control, I've been making some progress with https://github.com/rackt/history/pull/141, stemming from https://github.com/rackt/react-router/issues/2186. By moving beyond an inflexible browser-mimicking pushState
API, we ought to have the vocabulary to better define specific transition behavior at the point of initializing the transition.
One main, distinct, challenge of mobile routing and history management is that there isn't a single history stack, but rather multiple stacks that are parallel and/or hierarchical. Following something like rackt/react-router#743 (comment), an abstraction that offers an interface that works for native app development needs to have a different API.
We cannot abstract away the differences between web and mobile because the expected navigation/history paradigms are fundamentally different.
(post)
I don't have any experience with ios (user or developer), but in Android there's always a single back button and thus a sequence of screens you can navigate through, just like in the web. Navigating between tabs is pretty much like a REPLACE, and I can't think of an app that would use separate navigation stacks within tabs; instead they PUSH a completely new screen to the stack when performing an action within a tab that requires a different view.
From what I can tell, supporting multiple navigation stacks on Android would even require managing and persisting the stacks yourself.
My understanding is that the expectation on iOS is that top-level navigation is often via a tab bar, and each tab keeps its own history stack. The "swipe from left" back gesture is a "local" pop within the context of the current tab, and doesn't take you across tabs. It is indeed different from Android, which doesn't usually use a tabbed navigation pattern.
However, even on Android, with RN, nested navigators would still come up in the context of having an outer wrapping navigator for handling modals, and you'd still want to handle both "going back" within the scope of a modal with navigation, v closing the modal entirely (analogous to "going back" in the navigator containing the modal).
I think we can handle both with something like the same abstraction, though realistically targeting iOS is going to be more important here.
Thanks for clearing that up. For modals, and android "up navigation" in general, I think more sophisticated navigation methods like goBackTo(..)
would be enough though.
I think you'd want something like named history contexts.
Something like:
history.getContext('modals').goBack()
to close the modalhistory.goBack()
to implement Android back-buttonhistory.getTopContext().goBack()
to implement iOS swipe from left@taion I think I agree with you. I've found in my own work that there are cases where you need to define what going back actually means. Having multiple history contexts is the same solution I arrived at. I was going to do it so that each navigable component had its own history context, but I much prefer your idea of named contexts and being able to specify which context you want to work in.
Yeah, I initially was thinking in terms of indices, but that's a lot of book-keeping. Names are better in the context. Note that your location in this case is also going to really be a tuple rather than a path.
How so? I'm not against it, I'm just not sure I understand how that would work. I think paths may still be necessary - certain navigators will only be accessible in deeper levels of the application, and the application needs to know how to get there given one or more "paths".
This is why I think the fall through routing that I came up with works rather well. If you can keep track of history on top of that then it would work very will IMO. Right now I don't have history.
Sketching this out in a bit more detail:
<Router history={createNavigatorHistory()}>
<Route name="logged-out" component={LoggedOut} />
<NavigatorRoute name="base">
<NavigatorRoute name="tabs" component={Tabs}>
<NavigatorRoute name="tab1" component={Tab1}>
<IndexRoute name="tab1-scene1" component={Tab1Scene1} />
<Route name="tab1-scene2" component={Tab1Scene2} />
</NavigatorRoute>
<Route name="tab2" component={Tab2} />
</NavigatorRoute>
<NavigatorRoute name="modal" component={Modal}>
<Route name="modal-scene1" component={ModalScene1} />
<Route name="modal-scene2" component={ModalScene2} />
</NavigatorRoute>
</NavigatorRoute>
</Router>
Suppose right now my location is ['base', 'tabs', 'tab1', 'scene2']
and I got there from ['base', 'tabs', 'tab1', 'scene1']
.
A few possible options:
history.getTop().goBack()
-> ['base', 'tabs', 'tab1', 'scene1']
history.goBack()
-> ['base', 'tabs', 'tab1', 'scene1']
history.push('modal-scene1')
history.push('modal-scene2')
history.getTop().goBack()
-> ['base', 'modal', 'scene1']
history.goBack()
-> ['base', 'modal', 'scene1']
history.get('base').goBack()
history.push('tab2')
-> ['base', 'tabs', 'tab2']
history.push('tab1')
-> ['base', 'tabs', 'tab1', 'scene2']
history.getTop().goBack()
-> ['base', 'tabs', 'tab1', 'scene1']
history.goBack()
-> ['base', 'tabs', 'tab2']
Updated some routing stuff in the above example, please check current comment rather than emailed notification.
I haven't thought through many of the implementation details yet, but the two key things there are:
We'd need to figure out division of labor between router/history and <Navigator>
. Also, other general blockers in:
history
API which I think will make it a lot easier to manage the complexity needed for something like the aboveBy contrast IMO https://github.com/rackt/history/issues/16 is off-path which is why I closed it - swapping out sessionStorage
for AsyncStorage
while keeping the same API is orthogonal to what we actually need.
@taion suggestion of named contexts feels like it is heading in the right direction to manage history.
I'm unsure however if the route hierarchy is going to be enough to determine the implied routes for a given location. e.g. While logically we should agree tab1-scene1 and tab1-scene2 come before tab1-scene3 I'm not sure we could determine the implied routes from the JSX below without considering Route order important (which seem inflexible).
<NavigatorRoute name="base">
<NavigatorRoute name="tabs" component={Tabs}>
<NavigatorRoute name="tab1" component={Tab1}>
<IndexRoute name="tab1-scene1" component={Tab1Scene1} />
<Route name="tab1-scene2" component={Tab1Scene2}/>
<Route name="tab1-scene3" component={Tab1Scene3} />
</NavigatorRoute>
</NavigatorRoute>
</NavigatorRoute>
I don't think we can restructure the above JSX to have Scene 3 a child of Scene2 as one is not necessarily a child of the other when considering component composition and rendering.
So off the top of my head one option might be to allow a Route element to reference via attribute another element as its parent for implied route in navigation.
Another possibility might be to have a new element e.g. ImpliedRoutes
<NavigatorRoute name="base" component={Base}>
<IndexRoute name="scene1" component={Scene1} />
<ImpliedRoutes>
<Route name="scene2-base" component={Scene2-base}>
<IndexRoute name="scene2" component={Scene2}/>
<Route name="scene2-1" component={Scene2_1}/>
</Route>
<Route name="scene3" component={Scene3} />
</ImpliedRoutes>
<ImpliedRoutes>
<Route name="scene4" component={Scene4}/>
<Route name="scene5" component={Scene5} />
</ImpliedRoutes>
<Route name="scene6" component={Scene6} />
</NavigatorRoute>
Within an ImpliedRoutes element the order of the Routes would be considered important. Parent routes up the hierarchy would be implied if they have an IndexRoute.
That way the following locations are implied:
This has the added benefit of making sure all the implied routes being built up belong to the same Navigator (unlike using references which could be used to describe something that is invalid).
We still then need to be able to handle recursive routes e.g. friend of a friend of a friend. At the moment I can only think of using a reference to point back up to a point in the hierarchy where you can build back down again from.
@skrigs
Sorry, the implication wasn't that it came from the route order. I meant to imply that each context actually had an associated history stack, so going back would literally be a POP
on that stack... like a navigator.jumpBack()
or something. Ideally we build this to tie into <Navigator>
somehow.
We'd potentially need something separate like immediatelyResetRouteStack
, but I was just trying to outline a basic nav flow for now.
Am I missing something?
@taion I was referring to a previous comment by @skevy on implied routes. So using my previous example if on starting up the application via a link (/base/scene2-base/scene2-1) in a push notification then the history should start out with a stack on this navigator of:
Its easy from this to think the hierarchy is enough, but it isn't for example if the link visited was (/base/scene5) we need to be able to initialise a history:
The same thing in regards to reseting a navigators history could occur when triggering an event on another tab and pushing a route also.
My suggestion of a ImpliedRoute element was to allow ordering of the Route to have importance to support implied routes within the hierarchy and separate out different paths below the same navigator. However after further thought a standard Route element could be used for this and its component could just render nothing more than its children, though I think the ordering of the Route elements will need to be important to support implied routes.
I feel like it'd be simpler to handle deep links with history as just being deep links with history... e.g. actually just fully specify the history stack rather than implying it. If we're making location
objects not just be path/query/state tuples, we have the flexibility to explicitly state the history. Do we think that's going to be a lot more onerous than having implied routes? I'm concerned about flexibility with something like explicit implied routes.
Something like:
const deepLinkLocation = {
action: DEEP_LINK,
name: 'scene2-1',
stack: { ...whatever },
}
@taion you are correct anything else is inflexible. e.g. its possible that the routes in the stack have params in their URL that couldn't be derived from the route being visited at the top of the stack.
Do you think being explicit there is too tedious, though?
I never like to have to specify something multiple times, but the route definition doesn't include all the information that might be required as a general solution. I think the library needs the ability to explicitly state the implied routes as you have indicated (even though it feels a bit tedious). In some circumstances it is possible that they could be derived and maybe there is some benefit in a utility library to calculate them for these use cases.
I'm in the process of building a solution for my app taking into account design suggestions from this conversation. I now have the ability to configure nested navigators as per a react-router hierarchy and with history. I can push new routes and everything renders correctly and also got the back navigation working with it also hooked up to the hardware button in android. I'm working with Redux so my solutions depends on that architecture so it isn't a good decoupled general solution. I have also allowed for the navigation bar to be specified on a NavigatorRoute as a custom component.
I'm not directly keeping the Navigator stacks and history in sync, but only update the Navigator stack via the Router which receives the current route via props. Interestingly the shape of the Navigator stacks don't match my history. e.g. if I visit Tab1->Tab2->Tab1 the navigator will only have two scene Tab1 & Tab2 and switch between them, however the history will include an entry for all three(3) so the android back has the right behaviour. By switching between the mounted components their states are maintained e.g. textinput.
My next task is to get implied routes working. At this stage I'm not sure how or where I'm going to store the implied routes for a given route. Possibly since I'm using Redux I will define them within action creators and accept params to interpolate each of the routes.
I have decided to define the implied routes up front so on use I don't have to specify them. I have added a name property to all my routes (separate to path since I don't want params in their name) and then refer to them in an object as follows
const implied = {
tab1: ['base'],
scene2: ['tab1', '@tab1'],
scene3: ['scene2', 'scene1', 'base']
}
A @ name (e.g. scene2) is an indicator to the implied generator to use another implied definition for the continued chain avoiding the need for duplication, however it can also be specified in full (e.g. scene3)
Now when doing a pushDeep I can refer to only the destination URL.Since I use redux I will generate the implied routes and interpolated them within a middle layer. The Router is then explicitly informed of the implied routes through a props update. In history I will hold the interpolated implied routes, otherwise I would have to hold the params.
Therefore having react native router expecting implied routes to be explicitly defined isn't really an issue.
Makes sense - ideally we can imply some of this from the route structure, but it's not going to be a core thing.
I implemented this kind of api as an example in react-native. Basically building a wrapper of the current Navigator api.
Looks like this
<Router platform="ios">
<IndexRoute name="login" component={Login} />
<Route name="home" component={Home} />
</Router>
@skrigs is there any chance you can open up your redux routing solution, I am kinda in the same boat as you and interested to see how you did it.
Generally speaking, with RN 0.18, RR 1.1 pretty much out of the door, is there a more concrete roadmap to have a routing solution based on the navigator for React Native ?
hey all, I'm excited for RR to work with RN too, but until then here is a library my team wrote to help with routing - https://github.com/pressly/scene-router
Btw the new navigator with animated being used under the hood that @skevy has mentioned is now available: https://github.com/ericvicenti/navigation-rfc
I'm not closing this issue just yet, but talking to @skevy, my current thinking is that this is a no-go.
history
paradigm is not appropriate for native navigation; we have history
on the web side to shim around the browser APIs that we can't really control – the reducer-based paradigm from the repo above just makes a lot more sense when you can control all of history for yourselfPending any compelling points to the contrary, I'm going to close this as a WONTFIX
.
Also, the reducer-based paradigm gives much, _much_ better integration with Redux than is possible with the current style of API.
Again, this isn't really possible on the web side because the ultimate source of truth is the browser rather than the history
, but it's something to take advantage of for mobile.
Given the above, I'm going to close this issue for the time being.
I know this thread is old, but I wanted to add another note for the benefit of people looking for a solution that makes React Router possible on React Native.
I've been making some exciting progress with react-router-native—a lib that marries React Router to NavigationExperimental.
For those of you who are interested, here's the pitch:
React Router community decided that a reducer-based paradigm similar to that of NavigationExperimental is better suited to native navigation. Transition to a reducer-based paradigm is also being discussed for the web. On the other hand, NavigationExperimental has no intention to support a React Router-like interface and leaves the navigation state up to the developer to maintain.
A declarative API removes the need to write boilerplate code and speeds up development. React Router Native follows React's Learn Once, Write Anywhere principle by providing a superset of React Router's API that marries _React Router_ to _NavigationExperimental_.
Goals
- URL Driven Development
- Learn once, write anywhere: knowledge and proven idioms from react-router can be reused while extending them as necessary to allow navigation semantics unique to native platforms
- First class deep linking support
- Cross-platform
Please note that the address bar seen above is for development only and can be disabled by removing the addressBar prop from the <Router>
component.
Feedback is greatly appreciated.
🍺
How do you model the case like on the Instagram and Facebook apps where you can hit the same scene from multiple tabs in the tab bar?
@taion This is actually surprisingly easy to do:
const profileRoute = (
<Route path="profile" component={Profile} overlayComponent={ProfileNavBar} />;
);
<Router history={nativeHistory}>
<TabsRoute path="master" component={Master}>
<StackRoute path="/tab1" component={Tab1}>
<Route path="/home" component={Home} overlayComponent={HomeNavBar} />;
{profileRoute}
</StackRoute>
<StackRoute path="/tab2" component={Tab2}>
<Route path="/notifications" component={Notifications} overlayComponent={NotificationsNavBar} />;
{profileRoute}
</StackRoute>
</TabsRoute>
</Router>
This is possible because we only use React Router to render the navigational components and delegate rendering of user components to NavigationExperimental
. <Route>
, <TabsRoute>
and <StackRoute>
all have stock reducers with sensible defaults associated with them that spit out navigation states that can then be fed into NavigationExperimental
. So it is completely customizable per route via a custom reducer if you want to interpret a request completely independent of the browser behavior. That being said, I'm building a fairly complex app and the default reducers have been sufficient so far.
Hope this makes sense.
🍺
Right – that's where we ended up, but it didn't feel quite right. You _can_ replicate a bag of routes/scenes across multiple tabs in this manner, but it feels like a worse way of handling configuration, and doesn't make a lot of sense to me from the perspective of the actual URLs.
Essentially, my dislike for an API that requires nesting the same {childRoutes}
under multiple parent routes was the primary motivation for closing this issue in the first place.
https://react-router-website-xvufzcovng.now.sh/MemoryRouter mentions that
MemoryRouter is useful in non-browser environments like React Native.
Does it mean we can use react router in react native apps from version 4?
@nikhildaga Would also like to know what the status is. I get Expected a component class, got [object Object]
when trying to use Link
in RN
The other components seem to work which is good, but I can't get a link to work
You can create a custom Link
component. Check out the code in React Router's Link
to see how it works. The main reason it doesn't work out of the box is its usage of <a>
rather than something like View
or TouchableHighlight
.
React Router works with React Native, but you won't get nice NavigationController-esque transitions without a lot of effort.
Edit: I haven't tried it with v4, only v3.
React-Router v4 will help with this issue https://medium.com/@jschloer/react-router-v4-with-react-native-5f2005ab2a72#.56mt46jj9 way to go @jschloer
Most helpful comment
React-Router v4 will help with this issue https://medium.com/@jschloer/react-router-v4-with-react-native-5f2005ab2a72#.56mt46jj9 way to go @jschloer