reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.88k stars 15.27k forks source link

How to handle the state of third party components in a redux way? #404

Closed ms88privat closed 9 years ago

ms88privat commented 9 years ago

Hello Redux fellows,

I am switching from Angular to React(Native) and Redux (yeah!). So maybe I didn't get the whole picture yet but I don't have any idea how to handle the state of a third party component the redux way.

Example and my actual problem: The React-Native Navigator has some methods on it (e.g. push and pop) to handle the navigation between different scenes. So this Navigator Instance has its own state outside of the redux store.

What I am trying to archive is, that i can use the dev-tool time travel to navigate between the scenes in the end.

Thoughts: Is there a ways to handle the state inside the Navigator with a reducer function? Or should i somehow listen to changes on the state and trigger the appropriate Navigator method each time? (I'm concerned about out of sync situations between the redux store and the Navigator state, which may happen)

Greetings

mindjuice commented 9 years ago

I'm not familiar with React-Native Navigator, but from your description, it sounds similar in role to react-router. You can check out https://github.com/acdlite/redux-react-router to see how @acdlite solved the problem of keeping the two sides in sync.

ms88privat commented 9 years ago

Thank you for this hint. I have to investigate it later on. I keep this issue updated.

cesarandreu commented 9 years ago

You may wanna check out fluxible-router. It won't work with React Native, but it'll show you another example of how to keep both sides in sync.

aphillipo commented 9 years ago

Hello there @ms88privat - basically I need what is described here. I'm happy to help you build a wrapper for Navigator that works with redux...

ms88privat commented 9 years ago

@aphillipo thank you for remembering me on this topic! I kept it our of my scope, because for me it isn't that much important for now. I'm on vacation next week and after that if have to finish two projects really fast, so i don't think i have time for it until November. See you then.

aphillipo commented 9 years ago

Ah I got it all working yesterday. Easier than I thought!

Only problem is it's integrated into my application - should probably be pulled out into a separate module.

It's really nice being able to register routes to a singleton and the change to them by name (and redux action) rather than component. Not everything that Navigator is supported (not even back yet), but it does some clever stuff like check if routes are already mounted and calls popToRoute on the navigator instead of failing!

Will take a look at getting it into a library that you can use later this week.

gaearon commented 9 years ago

I'll close as there doesn't appear anything actionable for Redux here, but feel free to continue the discussion!

Mokto commented 9 years ago

@aphillipo Any update on this ? A redux/navigator library would be really nice :smile:

aphillipo commented 9 years ago

It's built, no tests but it works quite well. It feels weird coming from the web thinking about scenes as opposed to web pages/urls...

I'll put a link here when I get round to creating a repo later this week.

cutemachine commented 9 years ago

@aphillipo Would be great if you could share the solution. TIA

hayesgm commented 9 years ago

When I approached this, I did a standard action dispatch to change the state of currentScene in my store. Then I just switch to that new screen on componentWillReceiveProps in a router object (which acts as middleware). This doesn't really cover popToRoute, etc, but it's a simple way to hold the state in stores instead of the native component. E.g.

MyRouter.js

componentWillReceiveProps(nextProps) {
    if (nextProps.currentScreen && nextProps.currentScreen !== this.props.currentScreen) {
      this.refs.navigator.replace({component: nextProps.currentScreen})
    }
  }
render() {
  return (
    <Navigator
      ref="navigator"
      initialRoute={{component: this.props.currentScreen}}
      renderScene={this.renderScene} />
  )
}
aphillipo commented 9 years ago

Okay I'm not going to get time to do this... but I'll just paste the code into a Gist for now...

aphillipo commented 9 years ago

https://gist.github.com/aphillipo/0583601b0deab1e89dcf

Hope this helps even if it isn't a working example, it's pretty close.

aphillipo commented 9 years ago

Comment here if you want to know anything or want to get it into a repo.

mschipperheyn commented 9 years ago

Nice! First glance, you seem to be preloading all the registered routes, which seems memory inefficient. This might help: http://blog.mgechev.com/2015/09/30/lazy-loading-components-routes-services-router-angular-2/

aphillipo commented 9 years ago

I see what you mean but really this is only for react native and routes aren't mounted to the navigator until they are used. It's not anywhere near perfect what I've done.

I really should take the hour or two get it into a repo with a proper example.

mschipperheyn commented 9 years ago

Another thought: to keep it more in-line with a web version and to keep things organized, you might consider using url style paths as identifiers.

Regarding lazy loading. new AsyncRoute({ path: '/about', loader: () => System.import('./components/about/about').then(m => m.About), as: 'about' })

you could consider the new require.ensure of react-native (although the old fashioned callback there would be inconvenient).

aphillipo commented 9 years ago

I don't understand why it's important to add Lazy Loading on iOS? Do react native panels really use that much memory? I'm not even sure that separate bundles are possible with the way the React Native bundler works!

aphillipo commented 9 years ago

Yes - I 100% agree with you about URLs as paths/names/identifiers and I'd like for apps to be able to register schemes myapp://some/path/to/the/pages. But I'd rather launch.

mschipperheyn commented 9 years ago

For me, lazy loading is always important. It's more of a gut approach without knowing all the specifics of memory management on react-native or iOS. The idea of preloading all the top level components and maintaining direct references to them in a non garbage collectible way just doesn't feel right.

Also, a title attribute makes sense for your route registry (i8n, etc)

mschipperheyn commented 9 years ago

I also created a gist based on a modified version of your code. https://gist.github.com/mschipperheyn/4f5158fe4de48ea6b8d5

aphillipo commented 9 years ago

Love it - definitely an improvement in a few areas! Feel free to run with it in a git repo, happy to contribute :thumbsup:

sectore commented 9 years ago

@aphillipo / @mschipperheyn Nice one!

You might not have to store a list of routeNames within Router.js. Because React Native's Navigator has already a list of all routes, which you can grab by navigator.getCurrentRoutes()

So you can do something like this (instead of current implementation of Router.navigateTo(name)):

navigateTo(name) {
  const navigator = this.refs.navigator;
  const route = this.props.registry.getRouteByPath(name);
  const routeList = navigator.getCurrentRoutes();

        if (routeList.indexOf(route) > -1) {
            route.direction = BACK;
            navigator.popToRoute(route);
        } else {
            route.direction = FORWARD;
            navigator.push(route);
        }
    }

Also it seems to be a good idea to add a configureScene to a route (e.g. via an action) to grab it with configureScene

render() {
  return <Navigator
    ref="navigator"
    initialRoute={routerRegistry.getInitialRoute()}
    {...this.props}
    configureScene={(route) => route.sceneConfig || Navigator.SceneConfigs.FloatFromRight}
  />

So you can handle all kinds of NavigatorSceneConfigs, not just Navigator.SceneConfigs.FloatFromRight or Navigator.SceneConfigs.FloatFromLeft

Anyone want to start a repo for this?

aphillipo commented 9 years ago

Okay I guess I'll start a repo for this tonight incorporating the changes suggested seeing as it seems popular. I like your ideas @sectore - really nice about not needing to manage the route list ourselves. SceneConfigs are something should definitely be configurable on the route I think but how to handle the route direction?

Will then allow you guys to open issues and do pull requests rather than comment on a redux bug!

aphillipo commented 9 years ago

I'm also going to change the FORWARD and BACK constants to SCENE_PUSH and SCENE_POP. Any objections?

I wonder if it's always just routes that have a scene config (for PUSH and POP) too, the whole function on navigator should be replaceable with a prop which it is currently not... Maybe that's enough flexibility.

sectore commented 9 years ago

@aphillipo You don't have to manage the direction to go forward and then back. Because navigator.popToRoute(route) or navigator.popToTop() should play the last scene into opposite direction (without defining a SceneConfig). Just check official Navigator Examples.

Example:

FORWARD
-----------------------
navigator.push(route) // with Navigator.SceneConfigs.FloatFromRight
ViewA ---> float from right ---> ViewB

BACK
-----------------------
navigator.pop() // without any NavigatorSceneConfigs
ViewB ---> float from left ---> ViewA
aphillipo commented 9 years ago

Okay that's fantastic. Thanks!

burgalon commented 9 years ago

Hey @aphillipo @mschipperheyn! I like the GISTs you've created. I'm still missing 'passprops' so that we could navigate to a specific entity/model. Also the passprops need to be taken into account when selecting the popToRoute()

For example: /posts/1 => /posts/2

mschipperheyn commented 9 years ago

Yes, you are right. In the meantime, I have been fiddling a bit with code and made some changes. It's working pretty well for me now. I will fiddle a bit further based on the suggestions here, and update the gist and you can all have a go. Perhaps we should make this in to a project.

aphillipo commented 9 years ago

Yes I will 100% get to making a project from this. I will commit this to a repo now even if it doesn't work...

diffractometer commented 8 years ago

@aphillipo I'd love to see what you have going

burgalon commented 8 years ago

Hey guys, I created this https://github.com/burgalon/react-native-redux-navigator It's still work in progress and not fully working. The difference from the GISTs you came up with is that the entire route stack is saved in redux so that it can be restored to the exact same state and the back hierarchy will be restored (hopefully this will make stuff like Hot reloading work?) Additionally, the store saves the route passProps. I'm not happy with the sync logic as it doesn't give good control on the transitions and navigation... but I'm not sure what would be the right way to go about it

diffractometer commented 8 years ago

@burgalon cool - will check out thx.

@lynndylanhurley

burgalon commented 8 years ago

Hey guys, i added a working example of react-native-redux-navigator

gaearon commented 8 years ago

Relevant to the Navigator discussion: https://github.com/facebook/react-native/commit/a3085464f6ea36fc6b53cd0c711c048ffb1516f9

burgalon commented 8 years ago

I have removed my code for react-native-redux-navigator in favor of https://github.com/aksonov/react-native-redux-router