State Management on React [Part 1] - Context API
Tags: frontend, react, typescript, javascript
Published: 2023-05-29 22:37:35

Hi devs! How are you doing? 

![How are you doing meme gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uqqtcjjlbdigf5buto6z.gif)

In this article let's talk a little bit about state management on React. Actually this article will be done in several parts, where I will talk about some technologies and libraries for state management. &#128640;

**Summary:**
- [**Introduction**](#introduction)
- [**Show me the code**](#show-me-the-code)
- [**Trade offs**](#trade-offs)
- [**Conclusion**](#conclusion)

# Introduction

I hope you guys like of this content, because we are talk about a lot of techs. We are going to create a simple application but we will be able to see the code of the technologies and compare the implementation. 

I'll cover these technologies in each article:

- [**Context API (part 1)**](https://dev.to/kevin-uehara/state-management-on-react-part-1-context-api-4jig)
- [**Redux (part 2)**](https://dev.to/kevin-uehara/state-management-on-react-part-2-redux-n5f)
- [**Jotai (part 3)**](https://dev.to/kevin-uehara/state-management-on-react-part-3-jotai-i7f)
- [**Recoil (part 4)**](https://dev.to/kevin-uehara/state-management-on-react-part-4-recoil-55mp)
- [**Zustand (part 5)**](https://dev.to/kevin-uehara/state-management-on-react-part-5-zustand-1jl3)


We will use different projects but the same components, so in this first part I will create this components that we will use throughout other parts.

BUT, on this first part we will use the Context API where I would like to present one of the context api problems (re-render). We can controll the this re-render but it will be out of the box (like memo hooks etc). I will address this issue further on. 

Context API according to the documentation of React: 

> Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language

Previously the only way to share state in an application was using Redux (on initial of react, when we didn't even have hooks and use state on function components).

Now we can **provide** the state and share data on our tree of components in a simple way.

Now, how about we create our components and apply the context api? (**Remember that this components we will on the another parts of articles. So, I will create all components in this article and in the other articles we go straight to the point for the state manager code**)

To use the examples of state management, I will use the feature of dark mode change and a TODO list.

I will use Vite to create the project, using React + typescript, because of the simple boilerplate, setup and optimized by rollup. But it's just for personal preference.

And I will be using `yarn`

# Show me the code

First just install the chrome extension for React:

[React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi)


- Let's create the project:

```sh
yarn create vite react-context-api --template react-ts
```

- Install the dependencies:

```sh
cd react-context-api
yarn
```

- Running the project:

```sh
yarn dev
```

![Project Initial Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o481bfcfmm41s5unrjqp.png)

Let's create some directories for our components, hooks and providers.

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

- `components` it will be used for our default components
- `hooks` it will be used for our custom hooks
- `providers` it will be used for our Context API provider

On `components`, let's create our first component called `Button` on `Button/index.tsx` and the style using CSS modules `Button/Button.module.css`

### Button/index.tsx

```jsx
import button from "./button.module.css";

interface ButtonProps {
  label: string;
  onClick?: VoidFunction;
}

export const Button = ({ label, onClick }: ButtonProps) => {
  return (
    <button className={button.btn} onClick={onClick}>
      {label}
    </button>
  );
};

```

### Button/Button.module.css

```css
.btn {
    width: 150px;
    height: 40px;
    background-color: #1e40af;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    color: #fff;
    font-family: Arial, Helvetica, sans-serif;
    font-size: medium;
    margin-left: 4px;
}

.btn:hover {
    background-color: #2563eb;
}
```

Now, Let's create another button to change the theme (WHY?)
Because I want to show some problem of context api later...


### ButtonChangeTheme/index.tsx

```jsx
import { useTheme } from "../../hooks/useTheme";
import button from "./button.module.css";

interface ButtonChangeThemeProps {
  label: string;
}

export const ButtonChangeTheme = ({ label }: ButtonChangeThemeProps) => {
  return (
    <button className={button.btn} onClick={changeColor}>
      {label}
    </button>
  );
};
``` 

### ButtonChangeTheme/ButtonChangeTheme.module.css

```css
.btn {
    width: 150px;
    height: 40px;
    background-color: #4c1d95;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    color: #fff;
    font-family: Arial, Helvetica, sans-serif;
    font-size: medium;
    margin-left: 4px;
}

.btn:hover {
    background-color: #7c3aed;
}
```

Now on context components of our Todo form/list:
Creating the Input component:

### Input/index.tsx

```jsx
import style from "./Input.module.css";

interface InputProps {
  value?: string;
  label: string;
  placeholder?: string;
  onChange?: (evt: React.ChangeEvent<HTMLInputElement>) => void;
}

export const Input = ({ label, placeholder, onChange, value }: InputProps) => {
  return (
    <div className={style.inputContainer}>
      <p className={style.inputLabel}>{label}</p>
      <input
        value={value}
        className={style.input}
        type="text"
        name={label}
        id={label}
        placeholder={placeholder}
        onChange={onChange}
      />
    </div>
  );
};
```

### Input/Input.module.css

```css
.inputContainer {
    font-family: Arial, Helvetica, sans-serif;
    margin:0;
    padding:0;
    display: flex;
    flex-direction: column;
    width: 20%;
}

.inputLabel {
    font-size: 18px;
    margin-bottom: 4px;
}

.input {
    height: 30px;
    border: 1px solid #000;
    border-radius: 4px;
}
```

Now creating the `FormTodo` to integrate our Input and Button component:

### Form/index.tsx

```jsx
import style from "./Form.module.css";
import { Button } from "../Button";
import { Input } from "../Input";
import { useState } from "react";

export const FormTodo = () => {
  const [todo, setTodo] = useState("");

  const handleAddTodo = () => {
    addTodo(todo);
    setTodo("");
  };

  return (
    <div className={style.formContainer}>
      <Input
        value={todo}
        label="Todo"
        onChange={(evt) => setTodo(evt.target.value)}
      />
      <Button label="Adicionar" />
    </div>
  );
};
```

### Form/Form.module.css

```css
.formContainer {
    display: flex;
    align-items: flex-end;
    margin-left: 10px;
}
```

Now our list of todos:

### ListTodo/index.tsx

```jsx
import style from "./ListTodo.module.css";

export const ListTodo = () => {

  return (
    <ul>
      {[].map((todo) => (
        <li className={style.item} key={todo.id}>
          <label>{todo.label}</label>
          <i className={style.removeIcon} onClick={() => console.log('we will remove todo item')} />
        </li>
      ))}
    </ul>
  );
};
```

### ListTodo/ListTodo.module.css

```css
.item {
    display: flex;
    align-items: center;
}

.removeIcon::after {
    display: inline-block;
    content: "\00d7"; 
    font-size: 20px;
    margin-top: -2px;
    margin-left: 5px;
}

.removeIcon:hover::after {
    color: red;
    cursor: pointer;
}
```

Now the last component, just for use the dark mode container. We will modify to use the custom hook to change the style for dark mode. 
Let's create the `Content` simple component:

### Content/index.tsx

```jsx
interface ContentProps {
  text: string;
}

export const Content = ({ text }: ContentProps) => {
  return (
    <div
      style={{
        height: "30vh",
        width: "100vw",
        color: "#111827",
        backgroundColor: "#fff",
      }}
    >
      {text}
    </div>
  );
};
```

**FINNALY** all components that we will use along the articles are ready!!!


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

Now finnally, let's create our Context API provider for theme change:

Let's create a file on `providers/` called `theme/ThemeProvider.tsx` and a file with types called `theme/types.ts`:

### providers/theme/types.ts

```typescript
export interface ThemeProviderState {
  isDark: boolean;
  changeColor?: VoidFunction;
}
```

### providers/theme/ThemeProvider.tsx

```tsx
import { PropsWithChildren, createContext, useState } from "react";
import { ThemeProviderState } from "./types";

const defaultValues: ThemeProviderState = {
  isDark: false,
};

export const ThemeContext = createContext<ThemeProviderState>(defaultValues);

export const ThemeContextProvider: React.FC<PropsWithChildren> = ({
  children,
}) => {
  const [isDark, setIsDark] = useState<boolean>(false);

  const changeColor = () => {
    setIsDark(!isDark);
  };

  return (
    <ThemeContext.Provider value={{ isDark, changeColor }}>
      {children}
    </ThemeContext.Provider>
  );
};
```

The theme provider will "provide" the value if theme is dark and a function to change the theme color. Simple, isn’t it?

Just for a better files organization, let's create a new file on `hooks` called `useTheme` for our custom hook:

### hooks/useTheme.tsx
```tsx
import { useContext } from "react";
import { ThemeContext } from "../providers/theme/ThemeProvider";

export const useTheme = () => {
  return useContext(ThemeContext);
};
```

**easy peasy!**

Now, we can create a new provider for the manage the state of todos. So let's create on `providers` our `TodoProvider` with its type.

### providers/todo/types.ts

```ts
export interface ITodo {
  id: number;
  label: string;
  done: boolean;
}

export interface TodoProviderState {
  todos: ITodo[];
  addTodo: (label: string) => void;
  removeTodo: (id: number) => void;
}
```

### providers/todo/TodoProvider.tsx

```tsx
import React, {
  PropsWithChildren,
  createContext,
  useCallback,
  useState,
} from "react";
import { ITodo, TodoProviderState } from "./types";

const defaultTodoStateValues: TodoProviderState = {
  todos: [],
  addTodo: () => null,
  removeTodo: () => null,
};

export const TodoContext = createContext<TodoProviderState>(
  defaultTodoStateValues
);

export const TodoProvider: React.FC<PropsWithChildren> = ({ children }) => {
  const [todos, setTodos] = useState<ITodo[]>([]);

  const addTodo = useCallback((label: string) => {
    const newTodo = {
      label,
      id: Math.random() * 100,
      done: false,
    };
    setTodos((todos) => [...todos, newTodo]);
  }, []);

  const removeTodo = (id: number) => {
    const removed = todos.filter((todo) => todo.id !== id);
    setTodos(removed);
  };

  return (
    <TodoContext.Provider value={{ todos, addTodo, removeTodo }}>
      {children}
    </TodoContext.Provider>
  );
};
```

God bless Context API to provide a easy way to create and manage our states. And again, let's create our custom hook called `useTodo` on `hooks`

### hooks/useTodo.tsx
```tsx
import { useContext } from "react";
import { TodoContext } from "../providers/todo/TodoProvider";

export const useTodo = () => {
  return useContext(TodoContext);
};
```

Now let's use our providers wrapping our app on `main.tsx`:

### main.tsx
```tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { ThemeContextProvider } from "./providers/theme/ThemeProvider.tsx";
import { TodoProvider } from "./providers/todo/TodoProvider.tsx";
import { ButtonChangeTheme } from "./components/ButtonChangeTheme/index.tsx";
import { Content } from "./components/Content/index.tsx";

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <ThemeContextProvider>
      <ButtonChangeTheme label="Change theme" />
      <Content text="Hello World!" />
      <TodoProvider>
        <App />
      </TodoProvider>
    </ThemeContextProvider>
  </React.StrictMode>
);
```

And change the `App.tsx` to just use the `FormTodo` and `ListTodo` components:

### src/App.tsx
```tsx
import { FormTodo } from "./components/FormTodo";
import { ListTodo } from "./components/ListTodo";

function App() {
  return (
    <>
      <FormTodo />
      <ListTodo />
    </>
  );
}

export default App;
```

Now we need just replace in some components to use our custom hooks:

First, on `ButtonChangeTheme` component just add the custom hook useTheme and call on button the changeColor function:

### components/ButtonChangeTheme/index.tsx
```tsx
import { useTheme } from "../../hooks/useTheme";
import button from "./button.module.css";

interface ButtonChangeThemeProps {
  label: string;
}

export const ButtonChangeTheme = ({ label }: ButtonChangeThemeProps) => {
  const { changeColor } = useTheme();
  return (
    <button className={button.btn} onClick={changeColor}>
      {label}
    </button>
  );
};
```

on `FormTodo` adding the `useTodo` to add a todo item:

### components/FormTodo/index.tsx
```tsx
import style from "./Form.module.css";
import { Button } from "../Button";
import { Input } from "../Input";
import { useState } from "react";
import { useTodo } from "../../hooks/useTodo";

export const FormTodo = () => {
  const [todo, setTodo] = useState("");
  const { addTodo } = useTodo();

  const handleAddTodo = () => {
    addTodo(todo);
    setTodo("");
  };

  return (
    <div className={style.formContainer}>
      <Input
        value={todo}
        label="Todo"
        onChange={(evt) => setTodo(evt.target.value)}
      />
      <Button label="Adicionar" onClick={handleAddTodo} />
    </div>
  );
};
```

On `ListTodo` component use the `useTodo` to list all todos items (remember that we create with empty array):

### components/ListTodo/index.tsx
```tsx
import { useTodo } from "../../hooks/useTodo";
import style from "./ListTodo.module.css";

export const ListTodo = () => {
  const { removeTodo, todos } = useTodo();

  return (
    <ul>
      {todos.map((todo) => (
        <li className={style.item} key={todo.id}>
          <label>{todo.label}</label>
          <i className={style.removeIcon} onClick={() => removeTodo(todo.id)} />
        </li>
      ))}
    </ul>
  );
};
```

And for last, let's change the `Content` component that will use the `useTheme` to change the background color using css-in-js:

```tsx
import { useTheme } from "../../hooks/useTheme";

interface ContentProps {
  text: string;
}

export const Content = ({ text }: ContentProps) => {
  const { isDark } = useTheme();

  return (
    <div
      style={{
        height: "30vh",
        width: "100vw",
        color: isDark ? "#fff" : "#111827",
        backgroundColor: isDark ? "#111827" : "#fff",
      }}
    >
      {text}
    </div>
  );
};
```

Now we finnaly (for second time lol) finish second version of the app. **BUT** using context api.

![Sponge bob gif tired](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sp8g90ianwhkuvfxxlmr.gif)

Let's see on browser the result (if you need just run `yarn dev`):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6nyjw379hmaofmn3tb0a.gif)

Simple app, isn’t it? 
But now we can see all integrations working with providers and custom hooks...

That's all folks!

**HAAAA I'm kidding.** &#129313;
Let's talk about some problem of context api:

# Trade offs

Open the devtools and enable the highlight of react components render:

![Enable highlight react render component on devtools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5j65gls5xivj04jhltaa.gif)

Now Let's see the components render behaviour:

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

We can see that when we update the theme, all components bellow are re-render. And you can think: Just use a Memo! And yeah! It's a solution. But imagine when you have a lot of complex providers and components. Maybe can be harder to maintain the re-renders. 
And I consider a solution out of the box for state manager of context api.

The re-render just happens because on main.tsx we are wrapping all components using the `ThemeProvider`. So when the some state of Provider changes, all children components will re-render.

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


# Conclusion

Context API offers a simple and incredible way for we manage our global state of our application (and is native of react, we don't need to install any dependency), but we need to be careful with renderings and break into small providers to avoid the complex render unnecessary components.

This first part I used to explain the native solution to manage our state on React Applications. 

In the next parts, I will introduce some librarys that will provide the same global state management, but we will see different implementations, devX and fix the problem of re-render. (**Spoiler**) Let's build using Redux with redux toolkit (recent library) that will make implementation easier.

Remember, that all components that we created here, we will use in the next articles. So I will do not repeat the component creations, I will assume that you know what component we will change.

Now it's true! We really finish this first part of article!
That's all folks!

**I hope you enjoyed it and added some knowledge. See you in the next parts**

Some references:

- [React Context Docs](https://react.dev/reference/react/createContext)
- [Repository](https://github.com/kevinuehara/react-state-management)
- [Linkedin](https://www.linkedin.com/in/kevin-uehara/)