React Context API Explained

React context is an amazing way to share data inside React Component trees.

In React 16 there is a new context API introduced. It is an amazing way to share data inside a React Component tree. The problem we faced when Sharing data between a Parent and a child which is very deep down in the component tree we have to pass the data through multiple components until it reaches the child component we want to share data with.

We have to pass the data through all the levels of the tree or we can use Redux or any other state management library for solving this problem.

React Context solves this problem by providing a method called createContext() and when executed this method will provide us with two components Provider and Consumer

<Provider />

Provider is the parent component which will take a prop value and will provide this value to Consumer component used anywhere inside the Provider children tree.

We have to note here that Provider value can only be accessed If the Consumer component is used inside the hierarchy of the Provider component.

<Consumer />

When used inside the Provider children hierarchy the Consumer will receive the value of the nearest Provider above it.

The Consumer requires a Function as a child. This function will receive the value provided to the Provider and then you can do whatever you like with this value.

Let’s learn from an example

All of the above information is very good but let’s jump straight to the action by creating something which uses React new Context API.

Recently I have to implement Translation to a new React App. I used the React Context API in doing so. Let’s have a look at that code.

import React, { Component, PureComponent, createContext } from "react";

const { Provider, Consumer } = createContext('translations');

export class TranslationProvider extends Component {

    render () {

        const {
            translations = {},
            currentLanguage = '',
            children = ''
        } = this.props;

        return (
            <Provider
                value={{
                    translations,
                    currentLanguage
                }}
            >
                {children}
            </Provider>
        )

    }

}

In line 3 we created a context and extracted Provider and Consumer from the newly created context.
Then we created a new Component TranslationProvider which takes three props

1. translations: An object with all of the different translations
2. currentLanguage: The current language chosen by the user
3. children: Component children to render

Inside the render method of our TranslationProvider we extracted the translations and currentLanguage and passed it to our Provider as value.
Inside the Provider we rendered our children components.

Now it is time to use the values provided to our Provider inside our Consumer.

Just so you know our translations object will look something like this

const translations = {
    'en': {
        "welcome": "welcome",
        "login": "log in"
    },
    'cz': {
        'welcome': 'Vítejte',
        'login': 'Přihlásit se'
    }
}

<TranslatorComponent />

Let’s have a look at our TranslatorComponent code

export class TranslatorComponent extends PureComponent {

    render () {

        const {
            messageId = '',
            defaultMessage = '',
            language = '',
            children = ''
        } = this.props

        return (
            <Consumer>
                {
                    ({ translations, currentLanguage }) => {

                        const translatedMessage = translations[language || currentLanguage] && translations[ language || currentLanguage][messageId] || defaultMessage

                        return children(translatedMessage)
                    }
                }
            </Consumer>
        )

    }

}

Our TranslatorComponent takes four props

1. messageId: The identifier of the translation
2. defaultMessage: The fallback message which will be used if no translation found.
3. language: If we don’t want to use the global language we can overwrite it using this prop.
4. children: This will be a Function as a child. We will pass the translated message to this function for further use.

Inside the render method of our TranslatorComponent we have used the Consumer and passed it a function as a child. Inside that function, we get two values translations and currentLanguage 
We search the translations for the messageId provided in the props if found pass the result else pass the defaultMessage to our child function.

That’s it for TranslatorComponent. Let’s see our components in action

import React, { Component } from 'react';

import { TranslationProvider, TranslatorComponent } from '../path/to/Translator.js'

const translations = {
    'en': {
        "welcome": "welcome",
        "login": "log in"
    },
    'cz': {
        'welcome': 'Vítejte',
        'login': 'Přihlásit se'
    }
}

export default class MainApp extends Component {

    render () {

        <TranslationProvider translations={translations} currentLanguage={'cz'}>
            <Home />
        </TranslationProvider>

    }

}

class Home extends Component {

    render () {

        return (

            <Header />

        )

    }

}

class Header extends Component {

    render () {

        return (

            <div>
                <TranslatorComponent messageId="welcome">
                    {translatedWord => <p>{translatedWord}</p>}
                </TranslatorComponent>
            </div>

        )

    }

}

We have used TranslationProvider inside our MainApp and passed translations and currentLanguage to it. 

Then two level down inside our Header we have used the TranslatorComponent and passed it messageId and Function as a child.

Inside that function, we will get our Translated word and then we can use it to do whatever we want to do with it.

Most of the code is self-explanatory but if you still find an issue or have questions about the code feel free to ask in comments.

Conclusion

React Context is a great way to share global data needed by our React applications. We don’t have to use redux or any other state management library to solve our “Share data multiple levels down” problem.

Function as a child ( Render props ) is a technique to share code or logic between multiple components.

So that was it for this article. If you have any questions and suggestions feel free to comment.

Really loved this article?

Then subscribe to my blog. You will receive articles like this one directly in your Inbox frequently.

Here are more articles for you.

Leave a Reply

Your email address will not be published. Required fields are marked *