Sharing Data Across your React App with React Context
Tags: react, typescript, webdev
Published: 2023-02-12 15:44:58

It's very common to need to share data across different parts of a React application, and React Context can be a very useful method for doing this when the data being shared doesn't change very often.

In this post, we'll build a small React application that uses React Context to share the user's data with it's different components.

I highly encourage you to follow along on your own, but if you get stuck, or just want to see the code without working through each step, you can refer to the [GitHub repo](https://github.com/iamthe-Wraith/xample-react-context) I am sharing along with it.

## Before Continuing
In this post, I assume that you're already familiar with the basics of React Context, and the problem it seeks to address. So we will only be focussing on it's implementation within an application. If you're not yet familiar with React Context and the problem it solves, [freeCodeCamp](https://www.freecodecamp.org) has a great [article](https://www.freecodecamp.org/news/react-context-for-beginners/) I would highly recommend you check out!

I would also like to stress that React Context is one of many possible solutions, and your specific use case will determine if it's the RIGHT solution for you. So please do not take this post as a "one size fits all" answer.

With all that out of the way, let's dive in!

## Setup
To start, let's create our project. I'll be using [Vite](https://vitejs.dev/) for this, and will use their `react-ts` template preset to create a new React project using Typescript. (if you don't want to use Typescript, you can use Vite's `react` template preset instead. Just know that your code will vary slightly from mine throughout this post.) (see Vite's [Getting Started](https://vitejs.dev/guide/) page for more information.)

Create the project by running the following command in your terminal:

```
npm create vite@latest xample-react-context -- --template react-ts
```

then run to install the project's initial dependencies:

```
cd xample-react-context && npm install
```

Now that we have a fresh project, let's add a couple of dependencies we'll use later.

```
npm i react-router react-router-dom
```

Let's also get rid of some of the boilerplate code that was generated by Vite.

We won't need the default styling so let's remove the `/src/App.css` file:

```
rm src/App.css
```

And replace all the code inside of `/src/index.css` with:

*/src/index.css*
```css
* {
  margin: 0;
  padding: 0;
  color: #f1f1f1;
  box-sizing: border-box;
}

body {
  min-width: 100vw;
  min-height: 100vh;
  background: #1f1f1f;
}
```

Next, replace all the code inside of `/src/App.ts` with the following:

```typescript
function App() {
  return (
    <div className="app">
      Hello World
    </div>
  )
}

export default App
```

Now if you run `npm run dev` you should see the following:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y6o1prinuqhamwqpqtd7.png)

Great, we now have a blank canvas to begin our work!

Refer to branch [001-setup](https://github.com/iamthe-Wraith/xample-react-context/tree/001-setup) if you're having any issues.



## Routes
Next, let's set up a few screens for ourselves to work with. For now, let's start with a home screen, a sign in screen, a profile screen, and a not found screen.

Create a new directory within `/src` called `screens`. This is where we'll store the root component for each of our screens.

When I create components, I like to have a directory for each one, and inside that directory are all the files that pertain to the specific component. I find this very useful, especially in larger projects where we might have many files for every component, such as a file for styles (if you separate them from your `.[j|t]sx` files), `.stories.tsx` to use with Storybook, and a `.cy.ts` for Cypress Component testing. We aren't going to get into all those files in this post, but I am going to use the same structure here, so I wanted to explain why I structure my apps this way.

Let's begin with a base level `Screen` component so all our pages will have the same structure.

Create a new directory in `/src/screens` called `Screen`, and add an index.tsx file inside it. Then add the following code:

*/src/screens/Screen/index.tsx*
```typescript
import { FC, ReactNode } from 'react';

interface IProps {
  children: ReactNode;
  className?: string;
}

export const Screen: FC<IProps> = ({
  children,
}) => {
  return (
    <div className='screen'>
      { children }
    </div>
  );
};
```

This component isn't doing much right now, but we'll change that a little later.

Now, let's create each of our individual screens. I'll start with the Sign In screen. So inside `/src/screens` create a new directory called `SignIn`, add an `index.tsx` file inside it, and add the following code to the file:

*/src/screens/SignIn/index.tsx*
```typescript
import { FC } from 'react';
import { Screen } from '../Screen';

export const SignIn: FC = () => {
  return (
    <Screen className='sign-in-screen'>
      Sign In Screen
    </Screen>
  );
}
```

Now the `Home` screen.

*/src/screens/Home/index.tsx*
```typescript
import { FC } from 'react';
import { Screen } from '../Screen';

export const Home: FC = () => {
  return (
    <Screen className='home-screen'>
      Home Screen
    </Screen>
  );
}
```

Then the `Profile` screen.

*/src/screens/Home/index.tsx*
```typescript
import { FC } from 'react';
import { Screen } from '../Screen';

export const Profile: FC = () => {
  return (
    <Screen className='profile-screen'>
      Profile Screen
    </Screen>
  );
}
```

And lastly the `NotFound` screen.

*/src/screens/NotFound/index.tsx*
```typescript
import { FC } from 'react';
import { Screen } from '../Screen';

export const NotFound: FC = () => {
  return (
    <Screen className='not-found-screen'>
      Not Found Screen
    </Screen>
  );
}
```

Now that we have our pages, let's now create a router so we can navigate between them.

Create a new directory in `/src` called `routers`. This is where we'll store all the routers for our app. For this post, we will only have 1, but it's still good practice to keep things organized incase this app ever grows!

Inside `/src/routers` create a new file called `main.tsx`. This will house the code for the primary router of our application. Inside this file, and add the following code:

*/src/routers/main.tsx*
```typescript
import { Route, Routes } from 'react-router';
import { Home } from '../screens/Home';
import { NotFound } from '../screens/NotFound';
import { Profile } from '../screens/Profile';
import { SignIn } from '../screens/SignIn';

export const MainRouter = () => {  
  return (
    <Routes>
      <Route path='/' element={ <Home /> } />
      <Route path='/profile' element={ <Profile /> } />
      <Route path='/signin' element={ <SignIn /> } />
      <Route path='*' element={ <NotFound /> } />
    </Routes>
  );
};
```

Now that we have our routes setup, let's update `/src/App.tsx` by removing our initial Hello World content, and replacing it with the following:

*/src/App.tsx*
```typescript
function App() {
  return (
    <BrowserRouter>
      <MainRouter />
    </BrowserRouter>
  )
}
```

Notice that we wrap our `MainRouter` with React Router's `BrowserRouter` in the App component, and not inside our `MainRouter` component. This is a personal preference of mine to keep my router components clean. As you will see later, we are going to add more components to this file, and I prefer my router components ONLY contain router code.

If you run `npm run dev`, you should now be able to go to the following routes and see our screens in the browser:

*Note, your port may be different if you are running other Vite apps at the same time. To confirm what port you need, look in your terminal when you execute `npm run dev`. Vite will print out the url for you to use.*

- Home - `http://localhost:5173/`
- SignIn - `http://localhost:5173/signin`
- Profile - `http://localhost:5173/profile`
- NotFound - `http://localhost:5173/any-other-path`

Our app structure should now look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8dm53h6r8kxw46gilxp4.png)

Refer to branch [002-routers](https://github.com/iamthe-Wraith/xample-react-context/tree/002-routes) if you're having any issues.




## Navigation
Alright, we have our pages and routes set up, but it's a little annoying that we have to manually type our routes into the browser's address bar to view them. Let's add a navigation bar so we can more easily switch between pages.

We're going to add this navigation bar to our `Screen` component so it will automatically be displayed on all our pages.

To start, let's create a new directory inside `/src` called `components`. This is where we will store all our shared components for the app.

Inside `/src/components`, create a directory called `MainNav`, add an `index.tsx`, and add the following code.

*/src/components/MainNav/index.tsx*
```typescript
import { FC } from "react";
import { Link } from "react-router-dom";
import "./styles.css";

interface IProps {
  className?: string;
}

export const MainNav: FC<IProps> = ({ className }) => {
  return (
    <nav className={`main-nav ${className}`}>
      <Link to='/' className='logo'>
        Xample
      </Link>
      <Link to='/signin'>
        Sign In
      </Link>
    </nav>
  );
};
```

Let's also add some styling to make our nav more usable:

*/src/components/MainNav/styles.css*
```css
.main-nav {
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100vw;
  height: 5rem;
  padding: 0 1rem;
  background-color: #136f93;
}

.main-nav a {
  font-family: Arial, Helvetica, sans-serif;
  text-decoration: none;
}

.main-nav a:hover,
.main-nav a:focus-within {
  text-decoration: underline;
}

.main-nav a:hover {
  cursor: pointer;
}

.main-nav .logo {
  font-size: 3rem;
}
```

And now let's update our `Screen` component so all our pages display this new nav.

*/src/screens/Screen/index.tsx*
```typescript
import { FC, ReactNode } from 'react';
import { MainNav } from '../../components/MainNav';
import './styles.css';

interface IProps {
  children: ReactNode;
  className?: string;
}

export const Screen: FC<IProps> = ({
  children,
}) => {
  return (
    <div className='screen'>
      <MainNav />
      <div className='screen-content'>
        { children }
      </div>
    </div>
  );
};
```

*/src/screens/Screen/styles.css*
```css
.screen-content {
  margin-top: 5rem;
  padding: 1rem;
}
```

There we go, now if you run `npm run dev` you should see:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m80eomennrxdz8wosylb.png)

If you click the "Sign In" link, you should be redirected to the `SignIn` screen, and if you click the "Xample" logo, you should return to the `Home` screen.

We will add a link to the `Profile` screen once we are able to identify if the user is signed in.

Refer to branch [003-navigation](https://github.com/iamthe-Wraith/xample-react-context/tree/003-navigation) if you are having any issues.




## The UserSession Context
Okay, now it's finally time to add our React Context. But before we do, let's first consider the functionality we'll need.

- When the user is NOT signed in, they should see a "Sign In" link on the right side of the nav bar.
- When the user IS signed in, they should see their avatar along with a "Sign Out" link on the right side of the nav bar.
  - when the Avatar is clicked, the user should be redirected to `/profile`
  - when the "Sign Out" link is clicked, the user should be signed out, and redirected to `/signin`.
- If the user is NOT signed in and they land on the `/profile` route, they should be redirected to `/signin`.
- If the user IS signed in and they land on the `/profile` route, they should see their personal information and be able to edit it.
  - if the user edits the information and clicks "Save" their information should be updated throughout the app.
- If the user is NOT signed in and they land on the `/signin` route, they should see the `SignIn` screen where they can enter their username and password and sign in.
- If the user IS signed in and they land on the `/signin` route, they should be redirected to `/`.

From this we can see that we will need to,
- have a way of easily identifying if the user is signed in or not
- allow the user to submit their username and password to sign in
- get the user's information
- update the user's information
- allow the user to sign out

Alright, now that we know our requirements, we're ready to dive in!

We'll start by creating a new directory inside of `/src` called `contexts`. This is where we will store any contexts we create for our app. Inside this new directory, let's create a new file called `user-session.tsx`.

I name this file `user-session` because it will manage all data and functionality around a user's session, but feel free to name yours whatever you like!

Inside this file, add the following:

*/src/contexts/user-session.tsx*
```typescript
import { createContext, FC, ReactNode, useContext, useMemo, useRef, useState } from 'react';

interface IUserSession {
  isSignedIn: boolean;
}

interface IProps {
  children: ReactNode;
}

const UserSessionContext = createContext<IUserSession | null>(null);

export const useUserSession = () => useContext(UserSessionContext);

export const UserSessionProvider: FC<IProps> = ({ children }) => {
  const userSession: IUserSession = useMemo(() => ({
    isSignedIn: false,
  }), []);

  return (
    <UserSessionContext.Provider value={ userSession }>
      { children }
    </UserSessionContext.Provider>
  );
};
```

Let's step through this code so we know everything that's going on.

On Line 1 we're importing a few things from React. We'll touch on each of them in the next few steps.

Lines 3-5 are defining the structure our context will have using a Typescript interface. For now, our context will just have 1 property, but we will add more later.

Lines 7-9 are defining the props that our Functional Component below will take.

On Line 11 we're creating a new React Context using `createContext`. Notice we are using our interface from Line 3 here to specify the structure this context will have.

On Line 13 we're creating our own custom hook which we will use to access our context throughout our app. This is just a small convenience I like to do in my apps rather than calling `useContext(UserSessionContext)` everywhere. There is nothing wrong with using `useContext(UserSessionContext)` directly instead if that is how you prefer to write your code.

Lines 15-25 define a regular React Functional Component named `UserSessionProvider`, which we'll use to house all our user session specific logic.

Inside `UserSessionProvider`, on lines 16-18, we're creating a regular javascript object that's going to contain the properties the rest of our app is going to access. Notice I've wrapped the object in React's `useMemo` hook so we aren't creating a new object every time this component rerenders.

Finally, on lines 20-24, we're returning the context provider with our `userSession` object assigned as the context value.

Using this pattern, any children passed to our `UserSessionProvider` will be able to access our `UserSessionContext`.

So where do we put this new component?

We already know that every page of our app is going to need different pieces of the user's session data or to interact with that data in some way. So I think a good place for it would be at the top of our component tree, in our `App` component. Let's go update that now.

*/src/App.tsx*
```typescript
function App() {
  return (
    <BrowserRouter>
      <UserSessionProvider>
        <MainRouter />
      </UserSessionProvider>
    </BrowserRouter>
  )
}
```

Make sure you add our new `UserSessionProvider` INSIDE the `BrowserRouter`. This will be important later because we will need to utilize React Routers navigation methods, and these cannot be accessed outside of the `BrowserRouter`.

Notice that we are passing `MainRouter` as a child to our `UserSessionProvider` component. Since our `MainRouter` contains all our pages, now all of our pages (and all of their child components) will be able to access our context.

Now let's user our new context!

I think a good place to start would be in the nav. We know that when the user is signed in, they should see their avatar and a "Sign Out" link, and when they're NOT signed in, they should see a "Sign In" link. So let's wire this functionality up.

Inside `/src/components/MainNav/index.tsx` we first need to import our new custom hook `useUserSession`. (*If you didn't add this custom hook, you will need to import `useContext` from React, and the `UserSessionContext` from `/src/contexts/user-session.tsx` instead*). With it now imported, we just need to call it inside our Functional Component.

*/src/components/MainNav/index.tsx*
```typescript
export const MainNav: FC<IProps> = ({ className }) => {
  const userSession = useUserSession();

  ...
};
```

Lastly, we can check `userSession.isSignedIn` to know what content to render.

The `MainNav` component should now look like this:

*/src/components/MainNav/index.tsx*
```typescript
export const MainNav: FC<IProps> = ({ className }) => {
  const userSession = useUserSession()!;

  const renderRightContent = () => {
    if (userSession.isSignedIn) {
      return (
        <button>
          Sign Out
        </button>
      );
    }

    return (
      <Link to='/signin'>
        Sign In
      </Link>
    )
  }

  return (
    <nav className={`main-nav ${className}`}>
      <Link to='/' className='logo'>
        Xample
      </Link>
      { renderRightContent() }
    </nav>
  );
};
```

We should also update our `MainNav` styles to include our new "Sign Out" button.

*/src/components/MainNav/styles.css*
```css
.main-nav {
  position: fixed;
  top: 0;
  left: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100vw;
  height: 5rem;
  padding: 0 1rem;
  background-color: #136f93;
}

.main-nav a,
.main-nav button {
  font-family: Arial, Helvetica, sans-serif;
  text-decoration: none;
}

.main-nav a:hover,
.main-nav a:focus-within,
.main-nav button:hover,
.main-nav button:focus-within {
  text-decoration: underline;
}

.main-nav a:hover,
.main-nav button:hover {
  cursor: pointer;
}

.main-nav .logo {
  font-size: 3rem;
}

.main-nav .sign-out {
  background: none;
  border: none;
}
```

If we now run `npm run dev` the app should still look the same, with a "Sign In" link on the right hand side of the nav. This is because in `/src/contexts/use-session.tsx` we hard coded `isSignedIn` to be false. If we now go change it to `true`, we should see a new "Sign Out" button being displayed on the right side of the nav bar.

Great work!

Refer to branch [004-initial_context](https://github.com/iamthe-Wraith/xample-react-context/tree/004-initial_context) if you are having any issues.




## Signing In
We have our context wired up and working, but rather than hard coding if the user is signed in our not, we're going to want to allow the user to sign in themselves. Let's work on that next.

First, we're going to need a form for the user to enter and submit their username and password. For this, we're going to need a couple of inputs, and a button. Let's build those components first.

Create a new directory inside `/src/components` called `InputField`, add a new `index.tsx` file, and add the following code to it:

*/src/components/InputField/index.tsx*
```typescript
import { FC, InputHTMLAttributes } from 'react';
import './styles.css';

interface IProps {
  className?: string;
  id: string;
  inputProps: InputHTMLAttributes<HTMLInputElement>;
  label?: string | JSX.Element;
}

export const InputField: FC<IProps> = ({
  className,
  id,
  label,
  inputProps,
}) => {
  return (
    <div className={`input-field ${className}`}>
      { label && <label htmlFor={id}>{label}</label> }
      <input
        id={id}
        {...inputProps}
      />
    </div>
  )
}
```

Let's give it a little styling by creating `/src/components/InputField/styles.css` and adding the following to it:

*/src/components/InputField/styles.css*
```css
.input-field {
  display: flex;
  flex-direction: column;
  margin: 4px; 
}

.input-field * {
  font-family: Arial, Helvetica, sans-serif;
}

.input-field label {
  font-size: 1.2rem;
}

.input-field input {
  padding: 0.5rem;
  font-size: 1.2rem;
  border: 1px solid #3e3e3e;
  border-radius: 4px;
  background: #595959;
  outline: none;
}

.input-field input:focus-within {
  border: 1px solid #136f93;
  outline: 1px dashed #136f93;
  outline-offset: 1px;
}
```

Next, create a new directory inside `/src/components` called `Button`, add a new `index.tsx` file, and add the following code to it:

*/src/components/Button/index.tsx*
```typescript
import { ButtonHTMLAttributes, FC, ReactNode } from 'react';
import './styles.css';

interface IProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
}

export const Button: FC<IProps> = ({
  children,
  className,
  ...props
}) => {
  return (
    <button
      className={`button ${className}`}
      { ...props }
    >
      { children }
    </button>
  );
}
```

Let's also give our new button some styling. Create `/src/components/Button/styles.css` and add the following styles to it:

*/src/components/Button/styles.css*
```css
.button {
  margin: 4px;
  padding: 0.5rem 1rem;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 1rem;
  border: none;
  border-radius: 0.5rem;
  background-color: #136f93;
  color: #fff;
  cursor: pointer;
  outline: none
}

.button:not(:disabled):hover,
.button:not(:disabled):focus-within {
  background-color: #0d4e67;
}

.button:not(:disabled):hover {
  cursor: pointer;
}

.button:not(:disabled):focus-within {
  outline: 1px dashed #136f93;
  outline-offset: 1px;
}

.button:disabled {
  opacity: 0.5;
}

.button:disabled:hover {
  cursor: not-allowed;
}
```

With those components now built, let's create a form on our `SignIn` screen for the user to enter their username and password.

*We could make a separate component for this, but since this is the only place the user will sign in to our app, I think it's okay if we just do it in the `SignIn` screen.*

*/src/screens/SignIn/index.tsx*
```typescript
export const SignIn: FC = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const onSubmit: FormEventHandler<HTMLFormElement> = useCallback((e) => {
    e.preventDefault();
    console.log('Signing in...', username, password);
  }, [username, password]);

  return (
    <Screen className='sign-in-screen'>
      <form className='signin-form' onSubmit={onSubmit}>
        <h1>Sign In</h1>

        <InputField
          id='username'
          label='Username'
          inputProps={{
            type: 'text',
            placeholder: 'Enter your username',
            value: username,
            onChange: (e) => setUsername(e.target.value),
          }}
        />

        <InputField
          id='password'
          label='Password'
          inputProps={{
            type: 'password',
            placeholder: 'Enter your password',
            value: password,
            onChange: (e) => setPassword(e.target.value),
          }}
        />

        <div className='buttons-container'>
          <Button disabled={ !username || !password}>
            Sign In
          </Button>
        </div>
      </form>
    </Screen>
  );
}
```

*/src/screens/SignIn/styles.css*
```css
.signin-form {
  display: block;
  max-width: 30rem;
  margin: 5rem auto;
}

.signin-form h1 {
  margin-bottom: 1rem;
}

.signin-form .input-field {
  margin-bottom: 1rem;
}

.signin-form .buttons-container {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
```

Now that we have a form, let's add a function to our `UserSessionContext` where the user's data can be submitted when they click the "Sign In" button.

First, we'll need a couple pieces of state...one to hold our user data, and one to store if the sign in request is processing or not.

*/src/contexts/user-session.tsx*
```typescript
  const [data, setData] = useState<IUserData | null>(null);
  const [processing, setProcessing] = useState(false);
```

Then we'll need a function that will take the user's username and password, and make the request to the API to sign the user in.

*We aren't really going to make a request here. Instead, we're just going to use a `setTimeout` to simulate a request. Then we'll manually set the use's data.*

*/src/contexts/user-session.tsx*
```typescript
  const signin = useCallback((username: string, password: string) => {
    setProcessing(true);
    
    // using setTimeout to simulate a request
    console.log('POST /api/signin', { username, password });

    setTimeout(() => {
      setData({
        id: Math.random().toString(),
        username,
      });
      setProcessing(false);
    }, 500);
  }, []);
```

Once our fake request is done and the user's data has been set, we want to automatically redirect the user to the `Home` screen. To do this, we'll use React Router's `useNavigate` hook and call `navigate` as the last statement in our `signup` function.

Lastly, we need to add this new data to our `userSession` object so that the rest of our app can access these new properties and method:
*/src/contexts/user-session.tsx*
```typescript
...

interface IUserSession {
  data: IUserData | null;
  isProcessing: boolean;
  isSignedIn: boolean;
  signin: (username: string, password: string) => void;
}

...

  const userSession: IUserSession = useMemo(() => ({
    data,
    isProcessing: processing,
    isSignedIn: !!data?.id,
    signin,
  }), [
    data,
    processing,
    signin,
  ]);

...
```

Putting it all together:

*/src/contexts/user-session.tsx*
```typescript
interface IUserSession {
  data: IUserData | null;
  isProcessing: boolean;
  isSignedIn: boolean;
  signin: (username: string, password: string) => void;
}

...

export const UserSessionProvider: FC<IProps> = ({ children }) => {
  const navigate = useNavigate();
  const [data, setData] = useState<IUserData | null>(null);
  const [processing, setProcessing] = useState(false);

  const signin = useCallback((username: string, password: string) => {
    setProcessing(true);
    
    // using setTimeout to simulate a request
    console.log('POST /api/signin', { username, password });

    setTimeout(() => {
      setData({
        id: Math.random().toString(),
        username,
      });
      setProcessing(false);
      navigate('/');
    }, 500);
  }, []);

  const userSession: IUserSession = useMemo(() => ({
    data,
    isProcessing: processing,
    isSignedIn: !!data?.id,
    signin,
  }), [
    data,
    processing,
    signin,
  ]);

  return (
    <UserSessionContext.Provider value={ userSession }>
      { children }
    </UserSessionContext.Provider>
  );
};
```

All that's left is to call our new `signup` function when the user clicks the "Sign In" button.

*/src/screens/SignIn/index.tsx*
```typescript
export const SignIn: FC = () => {
  const userSession = useUserSession();
  ...

  const onSubmit: FormEventHandler<HTMLFormElement> = useCallback((e) => {
    e.preventDefault();
    userSession?.signin(username, password);
  }, [username, password]);

  ...
}
```

There we go, now we can enter a username and password (*you can use any username and password you want*), click the "Sign In" button, and we are automatically redirected to the `Home` screen. Notice that our "Sign In" link in the nav automatically updates to "Sign Out" because we're now signed in.

Refer to branch [005-signing_in](https://github.com/iamthe-Wraith/xample-react-context/tree/005-signing_in) if you are having any issues.




## Adding an Avatar
Now that we're able to sign in, and we have the user's data, we can add an avatar to the nav.

*Setting up real avatar uploading is outside the scope of this post, so here we're just going to use the first letter of the user's username.*

Let's first create a new `Avatar` component.

Create a new directory inside `/src/components` called `Avatar`, add a new file inside it called `index.tsx`, and add the following to it:

*/src/components/Avatar/index.tsx*
```typescript
import { FC } from "react";
import "./styles.css";

interface IProps {
  src?: string;
  username: string;
}

export const Avatar: FC<IProps> = ({ src, username }) => {
  if (src) {
    return (
      <div className='avatar'>
        <img src={src} alt={`${username}'s avatar`} />
      </div>
    );
  }

  return <div className='avatar default-avatar'>{username[0]}</div>;
}
```

And the styles...

*/src/components/Avatar/styles.css*
```css
.avatar {
  width: 3rem;
  height: 3rem;
  border: 2px solid #136f93;
  border-radius: 50%;
  background-color: #000;
  overflow: hidden;
}

.default-avatar {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 100%;
  text-transform: uppercase;
}
```

Now let's add it to our `MainNav`.

*/src/components/MainNav/index.tsx*
```typescript
    ...

    if (userSession.isSignedIn) {
      return (
        <div className='user-data-container'>
          <button className='sign-out'>
            Sign Out
          </button>
          <Link to='/profile'>
            <Avatar username={ userSession.data?.username! } />
          </Link>
        </div>
      );
    }

    ...
```

*/src/components/MainNav/styles.css*
```css
...

.main-nav .user-data-container {
  display: flex;
  align-items: center;
}

.main-nav .user-data-container > *:not(:last-child) {
  margin-right: 1rem;
}
```

There we go! We now have an Avatar displayed to the user when they are logged in. And when it's clicked, the user is redirected to their `Profile` screen!

Refer to branch [006-avatar](https://github.com/iamthe-Wraith/xample-react-context/tree/006-avatar) if you're having any issues.




## Signing Out
We now need to allow the user to sign out of our application. This process will be similar to what we did with the sign in functionality.

Let's go!

Inside `/src/contexts/user-session.tsx`, let's add a new `signout` function. Just like in the `signin` function, we're going to use a `setTimeout` to simulate a network request being made.

*/src/contexts/user-session.tsx*
```typescript
...

interface IUserSession {
  ...
  signout: () => void;
}

...

  const signout = useCallback(() => {
    setProcessing(true);
    
    // using setTimeout to simulate a request
    console.log('POST /api/signout');

    setTimeout(() => {
      setData(null);
      setProcessing(false);
      navigate('/signin');
    }, 500);
  }, []);

...

  const userSession: IUserSession = useMemo(() => ({
    ...
    signout,
  }), [
    ...
    signout,
  ]);

...
```

And now we can call `signout` when the "Sign Out" button is clicked in the nav.

*/src/components/MainNav/index.tsx*
```typescript
...

<button className='sign-out' onClick={userSession.signout}>

...
```

Way to go! The user is now able to sign out of our application!

Refer to branch [007-signing_out](https://github.com/iamthe-Wraith/xample-react-context/tree/007-signing_out) if you're having any issues.



## Adding a Private Route
As I mentioned earlier, the `/profile` route should only be accessible when the user is signed in. Currently, however, if the user were to manualy enter the `/profile` path into the address bar, they would still be able to view the page, regardless if they're signed in or not.

Let's fix this by making the `/profile` route private.

Inside of `/src/routers`, let create a new component called `PrivateRoute` with the following code:

*/src/routers/PrivateRoute/index.tsx*
```typescript
import { Navigate, Outlet } from "react-router";
import { useUserSession } from "../../contexts/user-session";

export const PrivateRoute = () => {
  const userSession = useUserSession()!;
  return userSession.isSignedIn ? <Outlet /> : <Navigate to="/signin" />;
}
```

It's a very simple component. All we are doing is checking if the user is signed in. If they are, the user is allowed to proceed to requested page. Otherwise, they are navigated to the `/signin` screen.

Now we need to updated our main router to use the new `PrivateRoute`.

*/src/routers/main.tsx*
```typescript
export const MainRouter = () => {  
  return (
    <Routes>
      <Route path='/' element={ <Home /> } />
      <Route path='/signin' element={ <SignIn /> } />

      <Route element={ <PrivateRoute />}>
        <Route path='/profile' element={ <Profile /> } />
      </Route>

      <Route path='*' element={ <NotFound /> } />
    </Routes>
  );
};
```

Voila! If we now manually enter `/profile` into the address bar when we are not signed in, we are redirected to `/signin`. And when we ARE signed in, we can still access `/profile`.

Nice job!

Refer to branch [008-private_route](https://github.com/iamthe-Wraith/xample-react-context/tree/008-private_route) if you're having any issues.




## Conclusion
Congratulations on making it through this little project with me!

There are still many optimizations that we could make to this application, but I think we're at a good stopping point.

I hope this post helped you gain a better understanding of how to share data across a React application using React Context.

Thank you for coding with me. Happy Hacking!