DEV Community

loading...

Learn to UseContext() with Hooks in 3 Minutes

curtiscodes profile image Dan Curtis ・3 min read

This is my first post on Dev.to! I'm surprised by the number of introductory React Context tutorials that use classes and useReducer(). I think classes and useReducer() overcomplicate things, so I thought I would write on useContext() without either.

We're going to create a super simple authentication app, and learn context in less than 3 minutes!

Authentication app

Context is like a global variable - or a global Hook - that's passed to every child. Context is a component that wraps around any other component. Every child component has access to it without being passed around through props. There are four steps to using context:

  1. Initiate context
  2. Provide the initiated context
  3. Implement context in our app
  4. Update context in our app

We're going to have an app that renders two components, one for authorized users and one for unauthorized users. This is the file structure:

  • index.js
  • App.js
  • UserContext.js
  • AuthApp.js
  • UnauthApp.js

That looks like a lot! Don't worry, I also created a CodePen with everything condensed into one file. It's 75 lines of code, including whitespace.

Step One: Initiate Context

Initiating context is super easy. We create a variable and set it to createContext(). We're going to set it to a default "fallback" value. If we left it blank, it would default to an empty object, which is okay too.

const UserContext = createContext({ name: '', auth: false });
// This also works: const UserContext = createContext();
Enter fullscreen mode Exit fullscreen mode

Step Two: Provide Context

Creating a function to provide our initiated context is the most complicated part.

We're going to call a provider function with children as a prop. This function will be the parent of every other component in our app. This allows us to provide any method we create within the provider function to any other component.

const UserProvider = ({ children }) => {
  return (
    <UserContext.Provider value={{ user, login, logout }}>
      {children}
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

We're creating a function that provides our newly initiated context to every child component. You probably are wondering where the value prop with user, login and logout is coming from. Those are the methods that will be accessible to any child component.

Let's create them:

const UserProvider = ({ children }) => {
  // User is the name of the "data" that gets stored in context
  const [user, setUser] = useState({ name: '', auth: true });

  // Login updates the user data with a name parameter
  const login = (name) => {
    setUser((user) => ({
      name: name,
      auth: true,
    }));
  };

  // Logout updates the user data to default
  const logout = () => {
    setUser((user) => ({
      name: '',
      auth: false,
    }));
  };

  return (
    <UserContext.Provider value={{ user, login, logout }}>
      {children}
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step Three: Implement Context in our App

Remember, context is a global variable. So we need to implement it as the highest level in our app, i.e. where React renders our app.

import React from 'react';
import ReactDOM from 'react-dom';
import { UserProvider } from './UserContext';
import App from './App';

ReactDOM.render(
    <UserProvider>
      <App />
    </UserProvider>
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

Step Four: Update Context

From here on out, we're going to be consuming context (i.e. using and updating it). Using context just requires importing it and calling it! In App.js, we import it as well as authenticated and unauthenticated components.

import React, { useContext } from 'react';
import { UserContext } from './UserContext';
import AuthApp from './AuthApp';
import UnauthApp from './UnauthApp';

function App() {
  const { user } = useContext(UserContext);

  return user.auth ? <AuthApp /> : <UnauthApp />;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

We simply import user context. Then because it's an object, we can access the auth property. Then we use a ternary operator (fancy if statement) to either return <AuthApp /> or <UnauthApp />.

AuthApp.js and UnauthApp.js similarly import the user context, and also methods to update the user context.

AuthApp.js

import React, { useContext } from 'react';
import { UserContext } from './UserContext';

function AuthApp() {
  const { user, logout } = useContext(UserContext);

  return (
    <>
      <h1>Hello, {user.name}!</h1>
      <button onClick={logout}>Logout</button>
    </>
  );
}

export default AuthApp;
Enter fullscreen mode Exit fullscreen mode

UnauthApp.js

import React, { useContext, useState } from 'react';
import { UserContext } from './UserContext';

function UnauthApp() {
  const { login } = useContext(UserContext);
  const [name, setName] = useState();

  return (
    <>
      <h1>Please, log in!</h1>

      <label>Name:</label>
      <input
        type="text"
        onChange={(event) => {
          setName(event.target.value);
        }}
      />
      <button onClick={() => login(name)}>Log in</button>
    </>
  );
}

export default UnauthApp;
Enter fullscreen mode Exit fullscreen mode

And that's a wrap. Let me know what you think!

A lot more goes into authentication that I did not cover. I'm currently creating a full-stack authentication example based on this post. I'll update this post with a link when it's done!

Discussion

pic
Editor guide
Collapse
petermarcoen profile image
Peter Marcoen

Thanks mate, I spent all day reading tutorials and getting nowhere. Your's finally made it work!

Collapse
curtiscodes profile image
Dan Curtis Author

I'm happy it clicked for you!