Prerequisites
- React > v16.8.0 (hooks are not supported below this version)
- Yarn package manager (personal preference is applicable)
State management has always been a crucial part of a React application. The performance and experience of our application are dependent on state management. Our application might not work as expected if we cannot handle and reflect state changes.
States in React JS
Why is the state so important? Imagine you are browsing a website. You see a dark mode toggle button and click on it. But, nothing happened, or only some part of the application is dark. It feels weird; there might be various reasons. It might be possible that the state is not changed or reflected throughout the application.
State lets us store some values that reflect the current condition of a component. React re-renders the required component when the state is changed after doing the necessary changes. React has introduced various hooks that let us use the state and other lifecycle methods simply and efficiently.
Let's see how state sharing is done in a modern React application.
State Sharing in React Application
In a typical React application, we use props to share the states. This method allows us to share the state from parent to child components.
This leads to the problem of not being able to share states between the sibling components. For example, imagine the CartDisplay
component inside the App component. Here we cannot share the state from CartOperator
component to CartDisplay
component.
To solve this issue, we can do something called state lifting. It is lifting the state from a component to its parent component. After that, we can pass the state from the parent to both the child components.
This solves our issue, but what if we need to share state logic between a large number of components? Only using useState
and doing prop drilling, state lifting might not sound practical.
We can use React Context API and custom hooks to share state logic globally. We also have many external libraries for state management, like recoil, redux, flux, etc. We should always examine our needs before using React context. This means making the state global, which it actually needs to be. For example:
- Theme configuration that determines how the application will look depending on that configuration.
- Shortcut keys set up those trigger actions that are divided throughout the application.
- Authentication status, which might be used to render various components based on user.
With that caution in mind, let's start learning about React Context API.
React Context API
React Context provides a way to share/pass data (including states) throughout the component tree. This removes the burden of needing to pass down props manually.
When to use React Context?
- State that needs to be globally accessible.
- Just follow point number 1 strictly.
Create Context (Theme Context)
Let's create a context that will share the theme state throughout our application.
createContext
takes an optional parameter for assigning a default value. It is undefined if we pass nothing.
Context Provider (Theme Context Provider)
ThemeContext.Provider
takes in a value prop. This prop makes the desired state accessible to its children components.
In our case, we are passing down isDark
state and setIsDark
method. This is our setup for context that will make our theme state (isDark
) and method to change it ( setIsDark
) globally accessible.
As mentioned before, we should wrap our components with the context provider. Not doing so will use the default value of the context (i.e., undefined for our case). So, let's wrap our App Component with ThemeContextProvider
first.
Our context setup is completed. Let's look at how we can consume that context value using hooks.
Hooks
Hooks let us use state logic that can be implemented anywhere inside React functions. We can use hooks to separate component logic and make it reusable. Some examples of React built-in hooks are: useState
, useEffect
, useReducer
, etc.
Making our custom hook
Let's implement a custom hook that gives us the theme state and method to toggle it.
At first, we are using the useContext
hook from React, which takes in a context. This hook returns its value provided by the nearest provider, i.e., ThemeContextProvider
in our case. After that, we made a function to toggle the theme. Finally, we returned our theme state and the method to toggle it from our custom hook called useTheme
.
We can use this custom hook anywhere inside our React functions. Let's see the implementation below:
We used our custom hook to access our current theme state via isDark
variable. After that, we added a click event listener on the button. This event triggers the toggleTheme
method from our custom hook.
Conclusion
We can use React context API to make our state or data globally accessible. The context provider is responsible for passing down the value. All those components that subscribe to the provider will re-render when the state changes. When we have more context providers, we might re-render components that do not require the change. This is the main reason for the warning:
Custom hooks can be used to separate component state logic and make them reusable. They can be used only inside the top-level React functions.
I hope you learned something new through this blog. Subscribe to learn even more!