React Context API
Tags: beginners, react, codenewbie
Published: 2023-05-21 04:29:35

Today, I'm sharing my experience of leveraging the power of React Context API with the useContext hook for authentication in React. My current project is relatively small, with each main page handling its own state, but there's one crucial element that constantly needs to be passed down throughout the entire application: the USER. 

Authentication is a vital aspect of any application, ensuring that users have secure access to the appropriate features and data. Implementing React Context API and the useContext hook into my project, really made me realize how much prop drilling and state lifting could be eliminated. 

Before I get started I would like to point out that I did not use Context for the user's **account information**. This example is purely for Authentication for logging in and out of the app and having just enough data for authorization. To achieve this, I only needed to pass the user's ID and username, allowing control of which features are accessible and customize the user experience accordingly. On the backend, I used Rails and created a dedicated serializer to carefully manage the user information being sent to the front-end for authentication purposes. The aim is to ensure both security and a personalized experience for each user.

First, create a **contexts** folder in your src file in React app that contains a new .js file called AuthContext.js. 
_src/contexts/AuthContext.js_
 
Next to create a context for our user object call the createContext method in the file and create a provider component named `AuthContextProvider` that takes in props, formatted just like any other React component. (React snippets extension in VS Code, you can call `rafce`)

```react
import { createContext } from 'react'

const AuthContextProvider = (props) => {
    
  return (
    <AuthContext.Provider value={}>
    </AuthContext.Provider>
  )
}

export default AuthContextProvider
```
The component receives a value prop, which determines the desired state or data for the context. Next, create state for the user in the provider and plugging in a simple object for testing. (Don't forget to  import `useState`) Pass the user as the value for the `AuthContext`. Then, add the children for the AuthContext `{ props.children }`.

```react
import { useState, useContext, createContext } from 'react'

const AuthContextProvider = (props) => {
   const [user, setUser] = useState({user_id: 1, username: "James"})
   
  return (
    <AuthContext.Provider value={ user }>
        {props.children}
    </AuthContext.Provider>
  )
}

export default AuthContextProvider
```
We want to render an `AuthContext.Provider` with the value of the user and wrap all of the components' children allowing the the children to have access to the value that is passed in. ( `user` ) Now, in the _App.js_, I'm going to wrap my entire app with the provider because all of it needs the user in some way or passed down to their children. Make sure to import the `AuthContextProvider`!

```react
import AuthContextProvider from '../contexts/AuthContext';

function App() {
  
  return (
    <>
      <AuthContextProvider>
        <NavBar  />
        <Routes>
          <Route path="/" element={<Home  />} />
          <Route path="/signup" element={<SignUp />} />
          <Route path="/signin" element={<SignIn  />} />
          <Route path="/profile" element={<Profile />} />
          <Route path="/trucks" element={<Trucks />} />
        </Routes>
      </AuthContextProvider>
    </>
  );
}

export default App;
```

Now all of the child components should have access to the value of the `AuthContext.Provider`. To get access to this context, you need to use the `useContext` hook from react in the components that need the user (context) and pass in the context that was created. (AuthContext). This will return the value of the user. 

```react
import { useContext } from 'react'
import { useAuth } from '../contexts/AuthContext'

const Home = () => {
  const user = useContext(AuthContext)

  return (
    <div>
      <h1>{user.username}</h1>
    </div>
  );
}

export default Home
```

There is a way to shorten this since you will probably use it in a lot of components. Go back to your context file and create a **custom hook**. 

```react
import { useState, useContext, createContext } from 'react'

export const AuthContext = createContext()

const AuthContextProvider = (props) => {
    const [user, setUser] = useState({user_id: 1, username: "James"})

  return (
    <AuthContext.Provider value={ user }>
        {props.children}
    </AuthContext.Provider>
  )
}

export const useAuth = () => useContext(AuthContext)

export default AuthContextProvider
```
Here you can create a function called `useAuth()` that returns the output of `useContext(AuthContext)` (which is the value prop). Now when you go back to a child component, you can reduce your code and just call `useAuth()` instead of calling and importing `useContext`. see below:

```react
import { useAuth } from '../contexts/AuthContext'

const Home = () => {
  const navigate = useNavigate()
  const user = useAuth()

  return (
    <div>
      <h1>{user.username}</h1>
    </div>
  );
}

export default Home
```
Once you have it set up, I suggest testing it out to make sure you have it all working. Then you can move on to fetching your user data and setting the state. From there add your logIn and logOut functions. See below how I added the functions to the value object. You will pass them to the children the same way as above, only make sure you deconstruct so your component has access to them. 
`const { user, logOut } = useAuth()`

```react
import { useState, useContext, createContext, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'

export const AuthContext = createContext()

const AuthContextProvider = (props) => {
    const [user, setUser] = useState(null)
    const navigate = useNavigate()

    useEffect(() => {
      fetch("/auth").then((resp) => {
        if (resp.ok) {
          resp.json().then((newUser) => {
            setUser(newUser);
          });
        }
      });
    }, []);

    const logInUser = (userObj) => {
      setUser(userObj);
      navigate("/");
    };

    const logOut = () => {
      fetch("/logout", {
        method: "DELETE",
      }).then((resp) => {
        if (resp.ok) {
          setUser(null);
        }
      });
      navigate("/");
    };

  return (
    <AuthContext.Provider value={ {user, logOut, logInUser} }>
        {props.children}
    </AuthContext.Provider>
  )
}

export const useAuth = () => useContext(AuthContext)

export default AuthContextProvider
```
I was hesitant to implement useContext at first, but like most code... once you understand it, you wonder how you lived without it. I hope this can be helpful to someone, I'm always open to learning more so don't be afraid to leave a comment. 

--
**You can connect with me on** [LinkedIn](https://www.linkedin.com/in/rachelsmithstark/) **&** [GitHub](https://github.com/rstarksmith)

Resources: 
[React - useContext] (https://react.dev/reference/react/useContext)

[React useContext] 
(https://www.youtube.com/watch?v=5ianRgE5ByU)