Coder Social home page Coder Social logo

Comments (24)

ericvicenti avatar ericvicenti commented on May 2, 2024 5

I'd like to sort these bullets into a few rough categories.

Of these categories, I want to flesh out the framework features. The router fixes should be straightforward. And documentation is really about talking to people and making sure these use cases are documented without redux.

Missing framework feature

  • Sagas and async navigation. What are the specific use cases here?
  • Dynamic initial route. Race condition tradeoffs.
  • State persistence
  • Accessing screen state from router, controlling router behavior from screen instance

Routers/Reducers need more features

  • Idempotent navigate
  • Dynamic reconfiguration of tabs

Use case is not documented

  • Confusion about nested routes not working
  • Top level dispatch, for responding to notifications, opening deep links, or triggering actions from side effects
  • Document custom router use cases, such as blocking actions

from rfcs.

RobIsHere avatar RobIsHere commented on May 2, 2024 3

IMHO it's one of the problems of the library, that it tries to be a redux library and at the same time a non-redux lib. The outcome is: it's perfect for none of them.

I'm in favor of redux for its simplicity and testability with minimal effort. That's just my personal preference. So when I control navigation by redux, I can easily test that the navigations get done like the should by just looking at redux. No mocks and all their issues needed. Just some very simple tests. Thats also my saga use-case as a response to above: easiest possible testing.

To take it further: redux has just about 200 LOC, that's nothing. Integrating it in RN is an option. Then you could configure "use user specified redux store", otherwise it uses the internal one. So react-navigation could provide a really mind-blowing interface on redux and would get easier testability for free.

The "without-redux-api" could just be a facade or an adapter that simplifies the full-blown redux interface, using it under the hood. There could also be a expert-level and a normal-level facade.

That separation of concerns of the different apis could really clean a lot of things up, if well done.

from rfcs.

lucianomlima avatar lucianomlima commented on May 2, 2024 2

What's the plan here? Show a non redux usage for these questions?

from rfcs.

dantman avatar dantman commented on May 2, 2024 2

It's a side effect. But it's nice having access to the Redux DevTools in the react-navigation context. After I switched to using redux I started opening up the React Native Debugger and looking at the navigation portion of the redux state to debug things when react-navigation was behaving strangely.

It's thanks to those Redux DevTools and React Navigation's state being in Redux that I easily found out that a misconfiguration in my pretty deep and complex navigator hierarchy was resulting in as sort of memory leak, where old routes were retained but not visible instead of replaced.

from rfcs.

salujaharkirat avatar salujaharkirat commented on May 2, 2024 1

@brentvatne @ericvicenti thanks for listing down all the issues. I went through redux react-navigation documentation few weeks back and I was wondering why is redux actually required here. My use cases are mostly the one's you listed and as you said I was able to achieve all of them without using redux.

What I am wondering is that is redux required only if I want to gain expert-level control of the app or are there any specific use cases too?

Once again thanks a lot :)

from rfcs.

jamsch avatar jamsch commented on May 2, 2024 1

I personally use Redux (& Redux Saga) to handle the navigation state for the following:

  • Tracking visited screens with Redux middleware for analytics
  • Saga to handle a race condition between navigation events that head towards a different route and performing a HTTP request. (i.e. active request is cancelled if user navigates to a specific route)
  • Saga to perform a HTTP request and navigate afterwards in response to something like a log in request
  • AsyncStorage rehydration using redux-persist and populating the navigation state based on user authentication.
  • Preventing navigation to the same screen twice

How would one otherwise handle the navigation state outside of React Components?

from rfcs.

salujaharkirat avatar salujaharkirat commented on May 2, 2024 1

@jamsch you can refer to react-navigation/react-navigation#1439 (comment) to handle navigation outside the components.

from rfcs.

lxcid avatar lxcid commented on May 2, 2024 1

That might be able to work for me, but I also like to be able to handle all my state in a centralised state container like redux. I really like the idea that react-navigation plug so well to redux.

I try to avoid global service like what describe in the article, only do them for things I really have little control like websocket and apollo client. But I hook them up in middleware, which is very similar to how the article does it but I always felt its an hack.

I haven't done it yet, but I think having the option to write reducers or selectors that parse the state tree of redux navigation is also quite appealing to me.

You get to see the big picture of where the state of your navigation is together with all your other state so it help in debugging.

Much of my app is heavily dependant on redux so with everything being an action, so I kinda benefit from the seamless integration at the moment.

I think the cons for you guys is that if we depend on any of the internal state, you will easily break our implementation.

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

@lucianomlima - make sure that these use cases are covered and documented by features that don't require redux

from rfcs.

ericvicenti avatar ericvicenti commented on May 2, 2024

The "without-redux-api" could just be a facade or an adapter that simplifies the full-blown redux interface, using it under the hood. There could also be a expert-level and a normal-level facade.

This is very similar to the original design of the library. If you want to use redux, you can provide a navigation prop and get expert-level control of your application. If you want the easy mode (normal-level facade), then you can just render your navigator normally, and createNavigationContainer will take care of things. It doesn't use redux directly, but it doesn't need to, because as you point out redux is very simple. createNavigationContainer is also around 200 lines.

IMHO it's one of the problems of the library, that it tries to be a redux library and at the same time a non-redux lib. The outcome is: it's perfect for none of them.

For reasons you've already described, @RobIsHere, this should be fixable! Why is it imperfect? Lets fix those issues.

We have a new helper library by @Ashoat to help play better with redux. Maybe this helps the redux use case? What else are you struggling with?

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

can you folks who need redux right now comment on how the following PR would or would not solve your use case, and explain what your use case is in detail there so that we can factor it into api design? thanks!

from rfcs.

lxcid avatar lxcid commented on May 2, 2024

I don't have much thoughts but I just want to add my voice that I use redux and redux saga with react-navigation as well.

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

@lxcid - what for?

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

why not use redux / redux saga and just access the global navigator object from sagas, rather than dumping your state in redux as well? https://reactnavigation.org/docs/navigating-without-navigation-prop.html

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

indeed, we can break it at any time. additionally, another downside is that the redux implementation is much more difficult to optimize because we can't take care of it for you, you need to do it in user space

from rfcs.

jamsch avatar jamsch commented on May 2, 2024

Are we able to listen to the navigator's navigate events? I have something like the following which needs to know the current route the user's on while they're interacting with the app.

function* navigateEvent(cb) {
  const navState = yield select(navSelector);
  const currentRouteName = getCurrentRouteName(navState);
  // Checks if the user navigated to a screen that's allowed
  if (validRouteNames.includes(currentRouteName)) {
    yield cb();
  }
}

// Listens for a 'NAVIGATE' navigation event, checks if user navigated to an allowed route
function* navigateAway() {
  yield take(NavigationActions.NAVIGATE);
  yield call(navigateEvent, navigateAway);
}

// Listens for a 'BACK' navigation event, checks if user navigated to an allowed route
function* navigateBack() {
  yield take(NavigationActions.BACK);
  yield call(navigateEvent, navigateBack);
}

// Task get's executed, cancelled when user navigates to some route
function* handleTask(action) {
  const winner = yield race({
    task: call(doSomeHeavyTask, action.taskId),
    navigateAway: call(navigateAway),
    navigateBack: call(navigateBack),
  });

  // Cancel task if winner is either "navigateAway" or "navigateBack"
}

from rfcs.

Freddy03h avatar Freddy03h commented on May 2, 2024

Hello,

I currently try to use less redux to handle react-navigation in my app.
Before : each navigators was separate and handled by a reducer with separate objects ( appTabs, newsStack, seriesStack, etc).

Now I connected all Navigators and I stil keep redux to not open twice the same view (I don't like the key solution that can go back to a previous route, but it's not what I want to talk about).
I also tried SwitchNavigator inside a Tab to display a view if the user is not connected or a StackNav if connected :

  • TabNavigator
    • SwitchNavigator
      • NotConnectedView
      • StackNavigator

But in my case, the NotConnectedView in SwitchNavigator don't handle the auth action, it only receive a Boolean isUserConnected from redux. And it's not easy to handle the displayed view using focus and navigate. I only want a if … else.

I think my redux use case is more about having a way to rewrite navigation.state between two navigators.

In a route config it can be nice to replace screen: MyStackNavigator by this screen: (navigation, screenProps) => <MyStackNavigator navigation={navigation} screenProps={screenProps} /> so I can re-write the state. But It handle an error because there's no more static router to recreate the global router.

I create a wrapper component around my router to handle it, and end with something like this :

const CollectionStackNavigator = StackNavigator(
  {
    collection: {
      screen: CollectionView,
      path: 'collection',
    },
    ...
    notConnected: {
      screen: NotConnectedView,
      path: 'notConnected',
    },
  },
  {
    initialRouteName: 'collection',
  }
)

class CollectionStackNavigatorWrapper extends React.Component {
  static router = CollectionStackNavigator.router;
  static navigationOptions = null;

  render() {
    const { navigation, screenProps, userConnected } = this.props

    const connectedNavigation = {
      ...navigation,
      state: userConnected
        ? navigation.state
        : CollectionStackNavigator.router.getStateForAction(CollectionStackNavigator.router.getActionForPathAndParams('notConnected'))
    }

    return (
      <CollectionStackNavigator
        navigation={connectedNavigation}
        screenProps={screenProps}
      />
    )
  }
}

function mapStateToProps(state) {
  return {
    userConnected: selectorUserConnected(state),
  }
}

export default connect(mapStateToProps)(CollectionStackNavigatorWrapper)

It work well for this case with an attribut from redux store, but I also use it for other non-redux use case.
Giving parent navigation params to children using screenProps (example : a filter option) and also read a children navigation params value in a parent navigator (example: hide the TabBar for a specific param in a view route in inner stack).

I hope my app use case can help.

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

@dantman that’s the most compelling use case I’ve heard of so far!

from rfcs.

fatfatson avatar fatfatson commented on May 2, 2024

I use navigation with redux because I want the UI changes could be traced back as other normal states.
but I found it's difficult to achieve this:
every time I jump to a previse state, it will generate new action automatically
image

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

@fatfatson - sounds like we need to provide a tool that lets you do that without redux!

from rfcs.

hedgepigdaniel avatar hedgepigdaniel commented on May 2, 2024

I posted also here

Ideally you shouldn't need Redux integration to do these things.

Ideally in a react redux app, you should not need a separate library to maintain its own state separate from redux, which is meant to be the one central store of all state (and doing so makes many things much more difficult).

sounds like we need to provide a tool that lets you do that without redux!

Why reinvent the wheel? redux dev tools works great (including in react-native-debugger). Also, why have two separate streams of event when you could have just one?

from rfcs.

sibelius avatar sibelius commented on May 2, 2024

hooks + context?

from rfcs.

brentvatne avatar brentvatne commented on May 2, 2024

@hedgepigdaniel - do you store react-router state in redux in a browser? a lot of state is local to components as well. there are countless examples of state that doesn't go into redux. it's tempting to move everything to redux but i just don't think that's a good idea.

Why reinvent the wheel? redux dev tools works great (including in react-native-debugger). Also, why have two separate streams of event when you could have just one?

a more specialized tool can provide more value. it could be made to work with react-native-debugger as well. anyhow i'm not building this right now but if someone wants to try that'd be fun.

anyhow we no longer provide explicit support for storing react-navigation navigator state in redux. you're welcome to continue to do this, but we won't help you with it and we won't test against it. of course you can continue to use redux or whatever you want to manage the other state in your app.

from rfcs.

hedgepigdaniel avatar hedgepigdaniel commented on May 2, 2024

I don't use react-router - for my production apps I use redux-first-router, which synchronises URLs with redux actions of your choice, as well other things, like providing a simple trigger for side effects of Redux actions. I find that it leads to an app architecture that is much easier to understand and test (basically M/V/C with Redux/React/redux-first-router) than what tends to happen when using react-router (everything crammed into components). In the past I've also used redux-first-router on react-native with react-navigation (v1.0) but recently I've had to do quite a bit of experimentation and research to get react-navigation to render the state I pass to it rather than doing its own thing.

That article I linked in the other thread was a big one for me in terms of keeping state in Redux and moving side effects out of components. I think its a good idea to move any state into Redux that has some chance of being relevant to app logic. If you need to look at some state to answer questions like:

  • Do I need to do an API call to download extra data?
  • Do I need to display a dialog before leaving this screen/page?
  • What are the details of the user who's profile page I am currently viewing?

Then I would argue that that state should be in Redux. If you have state coming into a component from Redux, and separate routing state coming from other HOCs (withNavigation, withRouter, etc). Then you have to have code in the component that can combine that state and project what you need to render (where in a pure redux setup you can have a selector selectAnyThingYouWant(state) totally independent of the component). If you have component state thats related in any way, it gets worse. Now you need to combine all three sources of state on each render. Hello getDerivedStateFromProps and componentDidUpdate. So you've got maybe three separate sources of state, and code in the component to combine them. Then what if you need to perform side effects depending on that combined state, like downloading data from an API? Then you've got to trigger side effects in componentDidMount. Doing it there means you're more likely to leave any extra resulting state in the component instead of lifting it up to Redux where it belongs. Its more likely you'll have duplicated code combining the same types of state in different components, doing the same API calls for slightly different purposes, etc.

The process of state management, side effect handling, and rendering is all done in the component with no clean separation of concerns. Testing components like this is so difficult that I don't think anyone actually does it. You would have to mock out APIs, simulate complex asynchronous timelines, and pass in three different contexts just to find out if a component renders the right thing in the right situation. And that's just for one component, but there's a whole tree of them! If they have global side effects like loading data into redux, they are probably causally connected aswell, so some components might only work when other components are rendered first, etc.

Contrast that to an app where all significant state is stored in Redux (especially routing/navigation state). Side effects like API calls are triggered in response to Redux actions. Components don't process and mix up props from different state containers - instead selectors are used to pass in the state from redux in the shape that is convenient for the component. Suddenly testing is easy. Components don't have state, lifecycle handlers, side effects, etc. They are nothing but a pure function that takes props and renders something. Same with selectors. Its very difficult to get confused. And its easy to see what in your app is making API calls or having side effects. Instead of having to dig through a huge component tree and guess which component had what state and when and why and which lifecycle handler in it decided to do it, All you have to look at is the redux actions in the log and the side effects attached to them. If the UI looks wrong at any time, you can look at the redux state (including its history) easily and get a good idea of why that is the case.

a lot of state is local to components as well. there are countless examples of state that doesn't go into redux. it's tempting to move everything to redux but i just don't think that's a good idea.

Why though? There is state that I think is harmless to put in components - the kind of state that is ephemeral and isolated. Whether a dropdown is open, the state of an animation, etc. In that case you save a bit of typing by keeping in in the component. But really saving some typing is all you gain. If you keep important state with implications to your apps behaviour in multiple places, the consequence is that your entire app is difficult to reason about and debug.

a more specialized tool can provide more value. it could be made to work with react-native-debugger as well. anyhow i'm not building this right now but if someone wants to try that'd be fun.

What value would it provide? What I would like to see in a debugger is the current state. e.g. in a StackNavigator:

  • what is the current stack?
  • What is the history of push/pop etc that made it get to that state?
  • What triggered these actions and why?
  • Perhaps I can even time travel through past states of the app and interactively see how the app renders with a different navigation state or find the specific state where a bug is triggered.

AFAICT, Redux can already provide everything one would want from a specific tool. All that would be accomplished with a separate tool is introducing confusion about which Redux and navigation states happened at the same time (because there would be two logs of interleaved state changes, not just one), and making time travel difficult/impossible for the same reason.

anyhow we no longer provide explicit support for storing react-navigation navigator state in redux. you're welcome to continue to do this, but we won't help you with it and we won't test against it. of course you can continue to use redux or whatever you want to manage the other state in your app.

Sure. I've been working on it and it still seems to be possible, although it requires many leaps of faith and alot of experimentation. I guess I just want to say that I think it would be good to continue to support that simply if its possible, and I'm trying to explain why I think its important. I'm not sure what sort of questions people ask, but I can imagine some possible benefits of good redux integration in terms of maintenance of this library:

  • less logic to support. Instead of helping people with potential state management bugs, only rendering bugs are relevant. The question goes from "What did you do to all the nested navigators in what order" to "What props are you passing to the specific navigator that is not working?"
  • Easier testing. For the same reason apps are easier to test with a good MVC architecture. It's generally healthy IMO to have a separate view layer (components which simply render based on their props and attach interactions to event handler props). That's basically whats needed for redux integration. If it was easy to import a component from react-navigation that skipped all the routers, state management, etc and just rendered the nice tabs, handled swipe gestures or whatever, it would also be easier to write unit tests for the library.
  • Easier debugging. Going out on a limb here because I haven't tried working on react-navigation... but wouldn't it be nice to have a log of state changes in redux? What if the library provided a redux reducer, exported connected components that behave like they currently do, and also the wrapped unconnected components in case the user wants to manage the state differently? Wouldn't that help to find bugs, e.g. to see if the bug was in the state management or rendering component, and see exactly at which state transition it went wrong?

from rfcs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.