20

I created a multiscreen app using React Navigator following this example:

import {
  createStackNavigator,
} from 'react-navigation';

const App = createStackNavigator({
  Home: { screen: HomeScreen },
  Profile: { screen: ProfileScreen },
});

export default App;

Now I'd like to add a global configuration state using the new builtin context api, so I can have some common data which can be manipulated and displayed from multiple screens.

The problem is context apparently requires components having a common parent component, so that context can be passed down to child components.

How can I implement this using screens which do not share a common parent as far as I know, because they are managed by react navigator?

Tom
  • 7,011
  • 6
  • 34
  • 50
  • You can use Redux to manage you state. – Mahipal Jul 05 '18 at 10:21
  • 2
    Yes, thank you. I know about Redux, but supposedly the same thing can be done with the new Context api, so I'd like to do it with builtin features instead of adding an external package just for this. – Tom Jul 05 '18 at 11:16
  • 1
    i realise its a year late but i guess you could try doing this by creating a custom Navigator and passing the context providers when rendering the navigator, you can refer the doc for steps on how to create a custom navigator (https://reactnavigation.org/docs/en/custom-navigators.html). Please do revert is this works for you. – maximast Aug 27 '19 at 14:06

4 Answers4

26

You can make it like this.

Create new file: GlobalContext.js

import React from 'react';

const GlobalContext = React.createContext({});

export class GlobalContextProvider extends React.Component {
  state = {
    isOnline: true
  }

  switchToOnline = () => {
    this.setState({ isOnline: true });
  }

  switchToOffline = () => {
    this.setState({ isOnline: false });
  }

  render () {
    return (
      <GlobalContext.Provider
        value={{
          ...this.state,
          switchToOnline: this.switchToOnline,
          switchToOffline: this.switchToOffline
        }}
      >
        {this.props.children}
      </GlobalContext.Provider>
    )
  }
}

// create the consumer as higher order component
export const withGlobalContext = ChildComponent => props => (
  <GlobalContext.Consumer>
    {
      context => <ChildComponent {...props} global={context}  />
    }
  </GlobalContext.Consumer>
);

On index.js wrap your root component with context provider component.

<GlobalContextProvider>
  <App />
</GlobalContextProvider>

Then on your screen HomeScreen.js use the consumer component like this.

import React from 'react';
import { View, Text } from 'react-native';
import { withGlobalContext } from './GlobalContext';

class HomeScreen extends React.Component {
  render () {
    return (
      <View>
        <Text>Is online: {this.props.global.isOnline}</Text>
      </View>
    )
  }
}

export default withGlobalContext(HomeScreen);

You can also create multiple context provider to separate your concerns, and use the HOC consumer on the screen you want.

dehamzah
  • 1,243
  • 1
  • 10
  • 18
2

This answer takes in consideration react-navigation package.

You have to wrap your App component with the ContextProvider in order to have access to your context on both screens.

    import { createAppContainer } from 'react-navigation'
    import { createStackNavigator } from 'react-navigation-stack'
    import ProfileContextProvider from '../some/path/ProfileContextProvider'

    const RootStack = createStackNavigator({
      Home: { screen: HomeScreen },
      Profile: { screen: ProfileScreen },
    });

    const AppContainer = createAppContainer(RootStack)    
    const App = () => {
      return (
      <ProfileContextProvider>
        <AppContainer />
      </ProfileContextProvider>);
    }
René Michel
  • 699
  • 7
  • 9
0

https://wix.github.io/react-native-navigation/docs/third-party-react-context/

As RNN screens are not part of the same component tree, updating the values in the shared context does not trigger a re-render across all screens. However you can still use the React.Context per RNN screen component tree.

If you need to trigger a re-render across all screens, there are many popular third party libraries such as MobX or Redux.

-3

If you want the detailed tutorial you could follow the below link : Visit : https://www.thelearninguy.com/simple-react-native-context-api-detailed

A very long answer would be as follows.

import React, {Component} from 'react';
import {Text, View, Button} from 'react-native';

//make a new context
const MyContext = React.createContext();

//create provider component
class MyProvider extends Component {
    state = {
        name: "The Learnin Guy",
        age: 50
    };
    increaseAge = () => {
        this.setState({
            age: this.state.age + 1
        });
    };

    render() {
        return (
            <MyContext.Provider
                value={{
                    state: this.state,
                    increaseAge: this.increaseAge
                }}
            >
                {this.props.children}
            </MyContext.Provider>
        );
    }
}

class Person extends Component {
    render() {
        return (
            <View>
                <Text>This is Person Component</Text>
                <MyContext.Consumer>
                    {(context) => (
                        <React.Fragment>
                            <Text>Name: {context.state.age}</Text>
                            <Text>Age: {context.state.age}</Text>
                            <Button title="IncreaseAge" onPress={context.increaseAge} />
                        </React.Fragment>
                    )}
                </MyContext.Consumer>
            </View>
        );
    }
}

class Family extends Component {
    render() {
        return (
            <View>
                <Text>This is Family Component</Text>
                <MyContext.Consumer>
                    {(context) => (
                        <React.Fragment>
                            <Text>Age: {context.state.age}</Text>
                        </React.Fragment>
                    )}
                </MyContext.Consumer>
                <Person/>
            </View>
        );
    }
}

class App extends Component {
    render() {
        return (
            <MyProvider>
                <View>
                    <Text>This is App Component</Text>
                    <MyContext.Consumer>
                        {(context) => (
                            <React.Fragment>
                                <Text>Age: {context.state.age}</Text>
                            </React.Fragment>
                        )}
                    </MyContext.Consumer>
                    <Family/>
                </View>
            </MyProvider>
        );
    }
}

export default App;

Courtesy - https://www.thelearninguy.com