Skip to content
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

Using unstated with RNN and auth #5081

Closed
djMax opened this issue May 7, 2019 · 13 comments
Closed

Using unstated with RNN and auth #5081

djMax opened this issue May 7, 2019 · 13 comments

Comments

@djMax
Copy link

djMax commented May 7, 2019

Issue Description

I am using unstated for state management which requires that I surround the "root" component with a Provider component (unstated's Provider, not RNN). Before RNN, I just put this around the App component. But the auth examples I've found for RNN talk about launching an auth flow and THEN navigating to the App component. That wouldn't work in this case because it would cause a new Provider to be spun up which has all sorts of bad side effects.

I can imagine a setRoot with a stack that had the auth on it that just moved from the Welcome/Splash screen directly to the tab-based navigation of the main app, but I'm not sure why the auth examples wouldn't have done that instead if it was that easy.

How does one integrate React-context based state into RNN without a JS-side root component that's always in the hierarchy?

Environment

  • React Native Navigation version: 2.18.4
  • React Native version: 0.59.5
  • Platform(s) (iOS, Android, or both?): both
@jinshin1013
Copy link
Contributor

Isn't it sufficient to just wrap your screen with Unstated Provider when you register them? Like below:

Navigation.registerComponent(() => <UnStatedProvider><YourComp /></UnStatedProvider>)

Each screen you register is treated as root component (I apologise if I'm wrong here :p) so you do need to wrap each screen with the Provider, I'm not entirely sure about what side effect it can cause?

@djMax
Copy link
Author

djMax commented May 8, 2019

I tried that, but it didn't work at all. I got undefined is not an object (evaluating _react.React.createElement). Strangely, the top of the stack was DatePickerAndroid.android.js, and this is a blank app with a view with just a text component on it. But if I take the Provider out, it works. The side effect I'm worried about is constructing multiple React contexts (via the unstated provider) which you're not supposed to do. Basically various libraries require a stable root component, and RNN seems to forbid this. Am I misunderstanding that?

@djMax
Copy link
Author

djMax commented May 8, 2019

That was my own silly bug, fixed now (mental note, import { React } from 'react' in one out of 50 components is 1 too many). BUT, it does do what I was afraid of, which is create independent state for every screen, which is obviously bad. I don't fully grok React Context yet, but I think it presents some challenges with RNN.

@christianlippa
Copy link

I would definitely look into redux. There are many examples of it working with React Native Navigation and it provides you with global state management like you want.

@jinshin1013
Copy link
Contributor

jinshin1013 commented May 8, 2019

Not sure why Unstated Provider has that behaviour. I use react-apollo, react-apollo-hooks and mobx which all provide their own Provider and I wrapped these for all screens that are being registered with RNN and never had any side effect as they keep them the same instance. Also I created mobx rootStore with React.createContext and use React.useContext to use it in Functional Component, but I guess it keeps the same "context" because it's mobx? Would you care to share a demo repo with RNN and Unstated setup? I'm keen to have a look.

@djMax
Copy link
Author

djMax commented May 8, 2019

The problem is here:

https://github.com/jamiebuilds/unstated/blob/master/src/unstated.js#L163

Since all the RNN Provider instances end up siblings, they don't share state. I fixed it by basically copying this library (it's small) and making them all use "parentMap" and passing in a Map to the initial createContext function.

@jinshin1013
Copy link
Contributor

I had a quick play around with Unstated with RNN and wrapping Unstated Provider for each screen when registering worked with using Unstated Subscribe in a component. But it only works if your Unstated Container is exported as an instance so like below:

// RNN Provider to register each screen
import { Provider } from 'unstated'

class RNNHocProvider extends React.Component<Props> {
  return (
    <Provider>
      <WrappedComponent />
    </Provider>
  )
}

// Unstated Counter Container
import { Container } from 'unstated'

class CounterContainer extends Container {
  // snip
}
export const counterContainer = new CounterContainer()

// Your screen component 1
import { Subscribe } from 'unstated'
import { counterContainer } from 'path-to-your-counter-container'

const ScreenComp1 = () => (
  <Subscribe to={[counterContainer]}>
    {counter => <View />}
  </Subscribe>
)

// Your screen component 2
import { Subscribe } from 'unstated'
import { counterContainer } from 'path-to-your-counter-container'

const ScreenComp2 = () => (
  <Subscribe to={[counterContainer]}>
    {counter => <View />}
  </Subscribe>
)

Upon updating counter state in ScreenComp1 will be reflected in ScreenComp1.

@smooJitter
Copy link

@jinshin1013

Maybe this will help

#4517

@support
Copy link

support bot commented May 13, 2019

We use the issue tracker exclusively for bug reports and feature requests. This issue appears to be a general usage or support question. Instead, please ask a question on Stack Overflow with the react-native-navigation tag.

@support support bot closed this as completed May 13, 2019
@djMax
Copy link
Author

djMax commented May 13, 2019

(I would suggest it's a feature request, because I think the idea that there may need to be "a thing" at the top of the render hierarchy is not limited to unstated. Perhaps it's simply not possible with RNN, but if it is, then I'd argue it should be exposed)

@jinshin1013
Copy link
Contributor

It's more of how you architecture your app, you can pass the same instance of the provider when you are registering your screen to avoid having multiple instances of Provider. Kinda like the registerComponentWithRedux.

@smooJitter
Copy link

@jinshin1013 have you tried Unstated-next?

@jinshin1013
Copy link
Contributor

I had a brief play around, I had a success by passing the same instance of the Unstated Container to Subscribe worked without even needing to pass the same instance of Unstated Provider when registering RNN components. But in my work project, I recently refactored how it registers all the component by passing the same instance of mobx and apollo Providers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants