Authentication in React App with Context
Tags: react, javascript, webdev
Published: 2020-05-27 19:13:05

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.

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
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`.

```javascript
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.

```javascript
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.

```javascript
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>
};
```