DEV Community

Victor Magarlamov
Victor Magarlamov

Posted on

Authentication in React App with Context

To be honest I avoided using Context for a long time. I easily began to use hooks, but I did not immediately understand Сontext. In this article I will show you one way to use Context.

const App = () => {
  <Switch>
    <Route path=/profile component={ProfilePage} />
    <Route path=/login component={LoginPage} />
    <Redirect to=/login />
  </Switch>
};

Let's restrict access to the ProfilePage - only authenticated users can access this page. If the user is a guest, we redirect him to the login page.

const LoginPage = () => {
  const [redirectTo, setRedirectTo] = useState(null);

  const submitHandler = e => {
    e.preventDefault();

    const formData = new FormData(e.target);

    authenticate(formData).then(user => {
      if (user.authenticated) {
        this.setState({ redirectTo: /profile });
      }
    });
  }

  if (redirectTo) {
    return (
      <Redirect to={redirectTo} />
    );
  }

  return (
     <Form omSubmit={submitHandler}>
       <Form.Input required type=email name=email label=Email />
       <Form.Input required type=password name=password label=Password />
       <Form.Button type=submit />
     </Form>
  );
};

The authenticate method sends user credentials to the API. When we get a response, we redirect the user to the ProfilePage. All is well, except for one trifle - everyone can access to the ProfilePage without authentication. To fix this, we need a flag - a global variable - to indicate whether the user is authenticated or not. Let's create Context that allows us to send a flag to components.

import React, { useState } from react;

export const AuthContext = React.createContext();

export const AuthProvider = ({ children }) => {
  const [authenticated, setAuthenticated] = useState(false);

  return (
    <AuthContext.Provider value={{authenticated, setAuthenticated}}>
      {children}
    </AuthContext.Provider>
  );
};

export const AuthConsumer = AuthContext.Consumer;

Go to the App.js file and wrap the Switch into the AuthProvider. AuthContext.Provider allows us to pass the context value - the authenticated flag and the setAuthenticated method - to all child components.

import { AuthProvider } from ./authContext;

const App = () => {
  <AuthProvider>
    <Switch>
      <Route path=/profile component={ProfilePage} />
      <Route path=/login component={LoginPage} />
      <Redirect to=/login />
    </Switch>
  <AuthProvider>
};

And make changes to the LoginPage.

import React, { useState, useContext } from react;
import { AuthContext } from ./authContext;

const LoginPage = () => {
  const context = useContext(AuthContext);
  const [redirectTo, setRedirectTo] = useState(null);

  const submitHandler = e => {
    e.preventDefault();

    const formData = new FormData(e.target);

    authenticate(formData).then(user => {
      context.setAuthenticated(true);

Now we just have to subscribe to the changes and send off a guest.

import { AuthConsumer } from ./authContext;

const ProtectedRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={matchProps => (
    <AuthConsumer>
      {value => (
         <Fragment>
           {value.authenticated || (
             <Redirect to=/login />
           )}
           <Component {...matchProps} />
         </Fragment>
      )}
    </AuthConsumer>
 )} />
);

Consumer is a React component, that subscribes to Context changes. It takes the function as a child and passes the current Context value to it.
Finishing touch.

import { AuthProvider } from ./authContext;
import { ProtectedRoute } from./ProtectedRoute;

const App = () => {
  <AuthProvider>
    <Switch>
      <ProtectedRoute path=/profile component={ProfilePage} />
      <Route path=/login component={LoginPage} />
      <Redirect to=/login />
    </Switch>
  <AuthProvider>
};

Top comments (0)