Learn React Context for Beginners

In this tutorial we’re going to learn React Context in a beginner friendly way. First, download the React Context project source code and run npm install. If you get stuck, you can check out the “completed” branch to see the complete working code.

I’m assuming you know at least basic React at this point if you’re looking at React Context, so browse the app real quick and you’ll see it’s pretty simple. We have a login and sign up form. a dashboard for authenticated users and an UnAuthenticated area for logged out users.

The first thing we’ll do is create a basic AuthContext file with the following code:

src/AuthContext.js

import React from "react";
import { initialUsers } from "./data/users";

const AuthContext = React.createContext();

function useAuth() {
  const context = React.useContext(AuthContext);

  return context;
}

const AuthProvider = (props) => {
  return <AuthContext.Provider value={"something"} {...props} />;
};

export { AuthProvider, useAuth };

The above code is almost enough to get us started. Basically we have two parts to React Context. The “Provider” and the “Consumer”. The provider tells you which parts of the app are allowed to access the values in context. The “Consumer” is what allows you to access the values within the parts of the app that are wrapped by the consumer. Then the “value” prop you see in AuthProvider is where all the data is stored, like functions and properties.

So we create the context, store it in “AuthContext”, and then in the useAuth function we say “use the AuthContext”, and we store that in the context const and return it from useAuth. This allows us to access those values from useAuth.

We also create the Provider, which is what we use to wrap the parts of the app that need access to the context values.

Now let’s try to access the context. Change your src/App.js code to the following:

import "./App.css";
import Dashboard from "./Dashboard";
import UnAuthenticated from "./UnAuthenticated";
import { useAuth } from "./AuthContext";

function App() {
  const test = useAuth();

  console.log("test: ", test);

  return (
    <div className="App">{test ? <Dashboard /> : <UnAuthenticated />}</div>
  );
}

export default App;

We are pulling everything from the context and storing it in “test”. When you console.log test, you should get undefined because we haven’t said which parts of the app can access this context yet. Let’s make a friendly error message for context. Back in the src/AuthContext.js file, make your useAuth() function look like the following:

function useAuth() {
  const context = React.useContext(AuthContext);

  if (context === undefined) {
    throw new Error("useAuth must be used within an AuthProvider");
  }

  return context;
}

Now you should get an error like this:

to fix this, we need to wrap the App component (or whichever part of the app needs access to the context) with the AuthProvider. Edit your src/index.js file to look like this:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { AuthProvider } from "./AuthContext";

ReactDOM.render(
  <React.StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

We just imported the provider and wrapped the App, which now has access to everything from the value prop in the context.

the “test” console.log should now be logging out “something”

that’s pretty much it. Now you just create properties and functions and put them in the value prop in the Context. Let’s create our currentUser. In the src/App.js file:

import { useAuth } from "./AuthContext";

function App() {
  const { currentUser } = useAuth();

  return (
    <div className="App">
      {currentUser ? <Dashboard /> : <UnAuthenticated />}
    </div>

But we don’t have currentUser coming from useAuth yet! Well… Put a property with that name in the “value” prop in the src/AuthContext.js file:

const AuthProvider = (props) => {
  const [currentUser, setCurrentUser] = React.useState("");

  const value = {
    currentUser,
  };
  return <AuthContext.Provider value={value} {...props} />;
};

That’s it! Your currentUser is now accessible in the entire app. But now we need to use state to set the currentUser. Currently it’s just an empty string all the time. So let’s create a login function. Here’s the new AuthProvider function in src/AuthContext.js

const AuthProvider = (props) => {
  const [currentUser, setCurrentUser] = React.useState("");
  const [users, setUsers] = React.useState(initialUsers);

  const login = (email, password) => {
    const loggedInUser = users.find((user) => {
      console.log("testing user: ", user, "param email: ", email);
      return user.email === email;
    });

    if (!loggedInUser) {
      console.log("No user found!");
      return;
    }

    if (password !== loggedInUser.password) {
      console.log("Wrong Password!");
      return;
    }
    setCurrentUser(loggedInUser);
  };

  const value = {
    currentUser,
    login,
  };
  return <AuthContext.Provider value={value} {...props} />;
};

We added users state to set the users who have accounts in the “users” state. The login function takes an email and password, and tries to find the user by the parameters passed in. If it finds the user it stores them in loggedInUser. If it fails to find the user or the password does not match, it returns empty without setting the user.

If the user enters the correct details they will be logged in using the setCurrentUser() to assign the logged in user to currentUser.

Now we need to make sure the app can access the login function from the React Context, so we add the login function to the value const, and pass the value into the value prop, making it accessible to the parts of the app wrapped in the context.

Now in the src/Login.js file we need to import the login function:

import { useAuth } from "./AuthContext";

function Login() {
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const { login } = useAuth();

// ... find the <button> element

<button onClick={() => login(email, password)}>Log in!</button>

You’ll notice the state for email and password is already setup from the starter app. So we just need to enter a valid user. Check the existing data/ folder and we have two valid users. I’ll use bob@bob.com with a password of “password” to log in.

entering a valid email and password should get you this page:

Now let’s create the logout() function. Same old thing. Only to log a user out we need to set the user to something that evaluates to false. in src/AuthContext.js:

const logout = () => {
    setCurrentUser("");
  };

  const value = {
    currentUser,
    login,
    logout
  };

in Dashboard.js, we might as well update the welcome message while we’re here:

import { useAuth } from "./AuthContext";

function Dashboard() {
  const { currentUser, logout } = useAuth();

  return (
    <div>
      <h3>Welcome {currentUser.email}</h3>

      <button onClick={() => logout()}>Log out</button>
    </div>
  );
}

export default Dashboard;

We just created the logout function and used it in Dashboard.js. We also imported the current user to say hello to the user since they’re logged in.

Let’s do error handling with React Context. again in AuthContext.js:

const [error, setError] = React.useState("");

  const login = (email, password) => {
    const loggedInUser = users.find((user) => {
      console.log("testing user: ", user, "param email: ", email);
      return user.email === email;
    });

    if (!loggedInUser) {
      setError("No user found!");
      return;
    }

    if (password !== loggedInUser.password) {
      setError("Wrong Password!");
      return;
    }
    setError("") // if success, reset error.
    setCurrentUser(loggedInUser);
  };

// ... find the value object and add error

  const value = {
    currentUser,
    login,
    logout,
    error,
  };

Now in Login.js:

// in function Login

  const { login, error } = useAuth();

// ...

  <button onClick={() => login(email, password)}>Log in!</button>
      </div>
      {error && <h2 style={{ color: "red" }}>Error: {error} </h2>}

Is it making sense yet? Since we’re getting repetitive at this point, I’d like to suggest finishing the signup function on your own. You can see the full working version by checking out the “completed” branch from the repository.

Like this article?

Share on facebook
Share on Facebook
Share on twitter
Share on Twitter
Share on linkedin
Share on Linkdin
Share on pinterest
Share on Pinterest

Leave a Comment

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