Share Your Data With Context and useContext

Context in React

You’re now going to learn to work with the member of the hooks family that lets you easily use React Context.

What’s React Context? šŸ¤”

Context is a way of easily sharing props between components. It’s native to React, so there is no need to install anything else.

If you took the previous course, you’d have seen how toĀ pass simple props between parent and child componentsĀ and how toĀ use props to lift state up. But can you imagine what that would be like in a complex application, where to pass a prop to a child component, you’d have toĀ go through dozens of parent componentsĀ that don’t need it?Ā Ā 

This doesn’t look terribly efficient, does it?
This doesn’t look terribly efficient, does it?

Enter React Context, which lets youĀ get your data without having to pass it manually. To do this, wrap the parent component that is highest up in the component tree with a Ā ProviderĀ  . All child components will then be able to connect to the Ā ProviderĀ  , thereby accessing the props without having to go through all of the components in between. These child components are known as Ā ConsumersĀ  .

Now the component simply hooks into the Provider to access the Context.
The component hooks into the provider to access the context

The notion of quickly passing data between components is at the heart of many issues, ensuring code performs well and is readable. In particular, there are many discussions on how to manage state in a comprehensible way that performs well. This is known asĀ state management. Ring any bells?

Before hooks came along, using context was much more difficult. Count yourself lucky that the Ā useContextĀ  hook exists! šŸ˜Ā 

Get the Most out of Context and useContext

Now that you know what context is, let’s put it to work in the Shiny application.

Set Up Context

What data should we put in the context?

React’s documentation states that ā€œContext is designed to share data that can be considered global,ā€ for example, current authenticated user, theme, or preferred language.Ā 

Let’s start byĀ creating a dark mode for the app. As you’ll know, this is a theme using darker colors that gives your eyes a rest when looking at the screen.Ā 

To set this up, create a footer in whichĀ you add a button. InĀ  Ā /componentsĀ  , create aĀ  Ā Footer/index.jsxĀ  file that works in quite a basic way:

import styled from 'styled-components'
import colors from '../../utils/style/colors'

const FooterContainer = styled.footer`
   display: flex;
   flex-direction: row;
   align-items: center;
   justify-content: center;
   padding-top: 60px;
`

const NightModeButton = styled.button`
   background-color: transparent;
   border: none;
   cursor: pointer;
   color: ${colors.secondary};
`

function Footer() {
   return (
      <FooterContainer>
         <NightModeButton>Switch mode</NightModeButton>
      </FooterContainer>
   )
}

export default Footer

Use this button in the footer to switch to dark mode in the app. Now create the context provider for the theme. To do this, make a folder especially for context in Ā utils/contextĀ  . Then create an Ā index.jsx fileĀ  .

To start, import Ā { createContext }Ā  from Ā reactĀ  , and set upĀ theĀ context for the theme with:

export const ThemeContext = createContext()

Next use Ā ThemeContextĀ  :

export const ThemeProvider = ({ children }) => {
   const [theme, setTheme] = useState('light')
   const toggleTheme = () => {
      setTheme(theme === 'light' ? 'dark' : 'light')
   }

   return (
      <ThemeContext.Provider value={{ theme, toggleTheme }}>
         {children}
      </ThemeContext.Provider>
   )
}

We’ve created a component that wrapsĀ the parent component with the theme Ā ProviderĀ  . The Ā themeĀ  state and the function to modify it, Ā setThemeĀ  , are passed in the Ā valuesĀ  . This means that all the child components wrapped by the Ā ProviderĀ  will be able to access Ā themeĀ  and Ā setThemeĀ  .

It’s now time to useĀ theĀ ProviderĀ  at the highest level whereĀ components will need access to the context. Put it in Ā index.jsxĀ  at the Ā /src rootĀ  . That gives you:

ReactDOM.render(
   <React.StrictMode>
      <Router>
         <ThemeProvider>
            <GlobalStyle />
            <Header />
            <Switch>
               <Route exact path="/">
                  <Home />
               </Route>
               <Route path="/survey/:questionNumber">
                  <Survey />
                </Route>
                <Route path="/results">
                   <Results />
                </Route>
                <Route path="/freelancers">
                   Freelancers />
               </Route>
               <Route>
                  <Error />
               </Route>
            </Switch>
            <Footer />
         </ThemeProvider>
      </Router>
   </React.StrictMode>,
   document.getElementById('root')
)

Does this mean that we have to put theĀ Ā ProviderĀ  at the level ofĀ the Ā routerĀ  ? šŸ¤”

Well, not necessarily! As its name suggests, context ā€œcontextualizesā€ your data. Certain parts of the application need to know about certain parts of state, while others don’t. It’s up to you to decide where best to put it.

Access Your Data With useContext

But wait, what’s Ā useContextĀ  ?

useContextĀ  is a hook used for ā€œhookingā€ from a child component that has been wrapped by a Ā ProviderĀ  , providing easy access to the shared state.Ā 

Let’s give it a go.

First, use the Ā themeĀ  to make a very visible modification: changing the Ā background-colorĀ  across the whole application.

Before,Ā GlobalStyleĀ  was in theĀ index.jsxĀ  file at the Ā /srcĀ  root, but now move it into a separate file in Ā /utils/style/GlobalStyle.jsxĀ  . You’re also going to changeĀ  Ā GlobalStyleĀ  to a function componentĀ that allows you to use hooks (before, it was just a styled-component). That gives you:

function GlobalStyle() {
   return <StyledGlobalStyle />
}

Next, import Ā ThemeContextĀ  and Ā useContextĀ  with:

import { useContext } from 'react'
import { ThemeContext } from '../context/ThemeProvider'

Which lets youĀ get the theme:

function GlobalStyle() {
   const { theme } = useContext(ThemeContext)

   return <StyledGlobalStyle isDarkMode={theme === 'dark'} />
}

Pass an isDarkModepropĀ according to the theme activated.Ā 

InĀ StyleĀ  , use it like this:

const StyledGlobalStyle = createGlobalStyle`
   * {
      font-family: 'Trebuchet MS', Helvetica, sans-serif;
   }

   body {
      /* Here this syntax goes back to the same as
      background-color: ${({ props }) =>
      props.isDarkMode ? '#2F2E41' : 'white'};
      */
      background-color: ${({ isDarkMode }) => (isDarkMode ? 'black' : 'white')};
      margin: 0;
   }
`

Ā All that remains now is to tackle the final piece ofĀ the puzzle: implementing the button changes modes. ā˜€ļø/šŸŒ™

So let’s go back toĀ Footer/index.jsxĀ  . Just like earlier, importĀ  Ā ThemeContextĀ  and Ā useContextĀ  .

Then get theĀ action Ā toggleThemeĀ  and Ā themeĀ  with:

const { toggleTheme, theme } = useContext(ThemeContext)

You can use it just underneath:

function Footer() {
const { toggleTheme, theme } = useContext(ThemeContext)
   return (
      <FooterContainer>
      <NightModeButton onClick={() => toggleTheme()}>
         Switch mode : {theme === 'light' ? 'ā˜€ļø' : 'šŸŒ™'}
      </NightModeButton>
      </FooterContainer>
   )
}

Let’s test it out.

Amazing! 🤩

When we click, the background color changes as required
Now the background color changes with a simple click of a button

Great work: you’ve just created a context and used it with Ā useContextĀ  to create a dark mode in your application. šŸŽ‰

As I’ve said, you’re lucky. Before hooks came along, context was much harder to implement. Watch the demonstration below to see how it used to work. šŸ‘‡

Give It a Go!

It’s time to put all of this into practice. For this exercise, you’ll use context to get the results of your survey between pages.

Start the exercise on branchĀ P2C2-begin. In Ā context/index.jsxĀ  , you’ll find a ready-to-use Ā SurveyProviderĀ  .

InĀ  Ā /Survey/index.jsxĀ  , you’ll find the codebase for storing user responses in the context and buttons for entering responses.

Like in the first course, you’re using the spread operator to storeĀ the object. Remember learning that state is immutable in the chapter ā€œShare State Between Different Componentsā€? For this exercise, do the following:

  • Use Ā useThemeĀ  in your Ā Footer/index.jsxĀ  .

  • Use your Ā SurveyProviderĀ  in Ā src/index.jsxĀ  .

  • Save the results in your state from Ā pages/Survey/index.jsxĀ  .

  • Get the results in Ā /pages/results.jsxĀ  with Ā useContextĀ  , and display them in a simple Ā console.log()Ā  .

If you struggle, you’ll find all of the code for the solution on branchĀ P2C2-solution.

Let’s Recap!

  • Context is a React feature for sharing state between several parent and child components with the help of a provider.Ā 

  • useContextĀ Ā  is a hook that lets you easily ā€œhook intoā€ context, thereby accessing shared state.

  • Using context is one method of state management, which can be combined with other methods such as Redux.Ā 

So, how was using context in your application with the useContext hook?

In the next chapter, you’ll learn more about hooks by creating your own custom ones. You’ll even see how to create your own useFetchĀ  hook to call an API. Sounds like fun, doesn’t it? šŸ˜Ž

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best