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

Support Context api #4427

Merged
merged 30 commits into from
Dec 16, 2018
Merged

Support Context api #4427

merged 30 commits into from
Dec 16, 2018

Conversation

guyca
Copy link
Collaborator

@guyca guyca commented Dec 10, 2018

This PR adds improved support for React's Context api
As he playground app still used RN 51, I had to upgrade it first to RN 0.57.7 which turned out to be a bit of a challenge. I also took the opportunity to upgrade TypeScript to 3.2.

Context API

Navigation.registerComponent('navigation.playground.ContextScreen', () => (props) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <ContextScreen {...props} />
  </TitleContext.Provider>
), () => ContextScreen);

Obviously, using static context works as well without needing to wrap the component.

Redux

Navigation.registerComponent('navigation.playground.ReduxScreen', () => (props) => (
  <Provider store={reduxStore}>
    <ReduxScreen {...props} />
  </Provider>
), () => ReduxScreen);

Plain Component

Navigation.registerComponent('navigation.playground.MyScreen', () => MyScreen);

This pattern supports any library requiring the user to wrap the base component.

guyca and others added 7 commits December 2, 2018 14:17
ComponentWrapper.wrap was a static method which handled both regular components and components wrapped with Redux provider.
This change is needed to support React’s Context api. Context api shares similarities with redux, as components are also wrapped with a Provider.
The only difference is that the component can be wrapped with multiple providers.
As Context api is available since React 16.6.x, I need to upgrade the playground app to RN 0.57.5 which supports it.
Hopefully this won’t take too long.
Took me only 2 days to update…
Instead, the default callback is always invoked - what am I missing…

gods of TypeScript heed my call
@guyca guyca changed the title Wrap with providers Support Context api Dec 10, 2018
@guyca guyca added this to the 2.3.0 milestone Dec 10, 2018
@henrikra
Copy link
Contributor

henrikra commented Dec 10, 2018

Hmm can you add more examples to PR description? For example how to use the new registerComponent with Redux? Also with nothing: just plain component. So can we get rid of all Redux with this PR?

Wrapping registered components can be done by returning a functional component.

For example:
```
Navigation.registerComponent('navigation.playground.ContextScreen', () => (props) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <ContextScreen {...props} />
  </TitleContext.Provider>
));
```
@henrikra
Copy link
Contributor

And how the api is used if I just have plain component?

@guyca
Copy link
Collaborator Author

guyca commented Dec 11, 2018

@henrikra As usual, no change.

@henrikra
Copy link
Contributor

So basically we could deprecate all those redux wrappers? Aka we would not have any redux dependencies in this project?

@guyca
Copy link
Collaborator Author

guyca commented Dec 11, 2018

Yes exactly. I already modified redux tests to this pattern, we can deprecate registerComponentWithRedux now and remove it in the next major version.

@henrikra
Copy link
Contributor

This is good! Tell me when you are done so I can review this also! :)

@guyca
Copy link
Collaborator Author

guyca commented Dec 11, 2018

@henrikra PR's actually ready - I'm just trying to figure out why our CI is giving me such hard time. For some reason Detox times out...

@henrikra
Copy link
Contributor

@guyca Ok I can check it later today 👍

Meanwhile could you check my iOS PR? I really need your guys help and opinions on this #4434
This is very high prio for me at the moment ⚠️ I am grateful if you can review it 🙇

@guyca
Copy link
Collaborator Author

guyca commented Dec 11, 2018

@henrikra Absolutely - we'll review it tomorrow

@henrikra
Copy link
Contributor

Good to hear!

Hey since this PR is doing two things: can you specify files that has to be reviewed that are related to registerComponent? So people don't have to go through files that aren't related to that :D

@henrikra
Copy link
Contributor

henrikra commented Dec 13, 2018

Looks good! On suggestions

I think we could get the API from this

Navigation.registerComponent('navigation.playground.ReduxScreen', () => (props) => (
  <Provider store={reduxStore}>
    <ReduxScreen {...props} />
  </Provider>
));

to this

Navigation.registerComponent('navigation.playground.ReduxScreen', (props) => (
  <Provider store={reduxStore}>
    <ReduxScreen {...props} />
  </Provider>
));

Aka removing one arrow function. If you look lib/src/components/ComponentWrapper.tsx line 20 the OriginalComponentGenerator just calls it self without params so I guess there is no point of having that extra function there? Wouldn't it be more clear without it se we can just directly pass those components?

@guyca
Copy link
Collaborator Author

guyca commented Dec 13, 2018

@henrikra Thanks for reviewing!
I don't think that's change is possible as the arrow function returns a component, which in our case is a functional component.

Also, I had to change the api 😕

Navigation.registerComponent('navigation.playground.ContextScreen', () => (props) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <ContextScreen {...props} />
  </TitleContext.Provider>
), ContextScreen);

The motivation is that we need to hoist statics from the actual component to the wrapping component in order to support static options. Do you have a better suggestion for the api?

@henrikra
Copy link
Contributor

I think I have bit better idea. Could it be like this

Navigation.registerComponent('navigation.playground.ContextScreen', (WrappedScreenComponent) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <WrappedScreenComponent />
  </TitleContext.Provider>
), MyComponentScreen);

So basically MyComponentScreen is my screen component which can be what ever. And WrappedScreenComponent what is coming back from wrap in ComponentWrapper. And then as you can see in the example I can wrap it what I want. If I just have plain component then api is like this:

Navigation.registerComponent('navigation.playground.ContextScreen', (WrappedScreenComponent) => <WrappedScreenComponent />, MyComponentScreen);

@guyca
Copy link
Collaborator Author

guyca commented Dec 13, 2018

Navigation.registerComponent('navigation.playground.ContextScreen', (WrappedScreenComponent) => <WrappedScreenComponent />, MyComponentScreen);

This is a breaking change - we need to avoid breaking changes for now. The second argument needs needs to be a method which returns a Component, not an instance of a component.

@henrikra
Copy link
Contributor

Okay what about same points as I said before but with extra function

Navigation.registerComponent('navigation.playground.ContextScreen', () => (WrappedScreenComponent) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <WrappedScreenComponent />
  </TitleContext.Provider>
), MyComponentScreen);
Navigation.registerComponent('navigation.playground.ContextScreen', () => (WrappedScreenComponent) => <WrappedScreenComponent />, MyComponentScreen);

@henrikra
Copy link
Contributor

henrikra commented Dec 13, 2018

Or what about this kind of API: we provide HOC from library like withNavigation

// in component file
import {withNavigation} from 'react-native-navigation';

class MyCoolComponent extends React.Component {}

export default withNavigation(MyCoolComponent);


// in screen file
import MyCoolComponent from './MyCoolComponent';
Navigation.registerComponent('navigation.playground.ContextScreen', MyComponentScreen);

What do you think about this? Yeah it is breaking but we can change function name so it is NOT breaking :D This feels most flexible to me. What do you think?

guyca and others added 2 commits December 16, 2018 09:52
…entProvider

This prevents premature require of the Component which impacts performance
@guyca
Copy link
Collaborator Author

guyca commented Dec 16, 2018

Hey @henrikra, I think we should merge the feature now as I don't have much time to work on it anymore. Lets keep discussing the api in a new issue - we can consider this api experimental in the next few versions until we're both satisfied with it.

@henrikra
Copy link
Contributor

Sounds good

@guyca guyca merged commit 9d36521 into master Dec 16, 2018
@guyca guyca deleted the wrapWithProviders branch December 16, 2018 08:46
vshkl pushed a commit to vshkl/react-native-navigation that referenced this pull request Feb 5, 2020
The following PR introduces improved support for Context api and other api's which wrap the root view.

## Context api
Navigation.registerComponent('navigation.playground.ContextScreen', () => (props) => (
  <TitleContext.Provider value={'Title from Provider'}>
    <ContextScreen {...props} />
  </TitleContext.Provider>
), () => ContextScreen);

## Redux
Navigation.registerComponent('navigation.playground.ReduxScreen', () => (props) => (
  <Provider store={reduxStore}>
    <ReduxScreen {...props} />
  </Provider>
), () => ReduxScreen);

## Plain Component - not changed
Navigation.registerComponent('navigation.playground.MyScreen', () => MyScreen);

This PR also upgrades the TypeScript version to 3.2.0 and RN version used in the playground app to 0.57.7

* New Android build flavor - `reactNative57_7`
* Unit test coverage is disabled, for some reason it broke after upgrading to RN 0.57.7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants