The React Hooks Explained
Diana
Posted on September 15, 2024
React Hooks have simplified the way we write React components. With Hooks, we can manage state, handle side effects, and more, all within functional components. Introduced in React 16.8, Hooks allow us to use state and other React features in functional components, making our code simpler and more intuitive.
useState hook:
The most common hook is the useState hook. To understand useState, let's first try to understand what state is.
State refers to data that will change and needs to be tracked. So, let's see the structure of the useState
hook:
n this example, we have two states: name
on line 4 and displayName
on line 5. name
and displayName
are the state variables that we are trying to track. setName
and setDisplayName
are the update functions that will be triggered when we want to update the value of the state variables.
We start with an empty string for each state variable (as we see in the parentheses of each state on lines 4 and 5—the starting value is optional, but note that you will have an empty state if you do not provide a default value).
By convention, the update function name will always have a prefix of set followed by the state variable name.
The state name
is tracking what the user is typing in the input on line 17. Every time the value in the input changes, handleInputChange
is triggered, and inside this function, we set name with the current value using setName
.
Similarly, we have displayName
, which is the name that will be displayed when the user clicks the button we created on line 18.
Every time we change the input, the name
state is updated (I added a log so we can see it):
Only when we click on the button the displayName
will be set to the current value we have in the name
state. (what we see in the console is the name
state changing)
useEffect hook:
The useEffect
hook allows us to perform side effects in our application. In simple terms, side effects are things that happen as a result of some task we performed, like data fetching, updating the DOM, and more.
Let's understand the structure of the useEffect
hook:
IMPORTANT: It's guaranteed that the useEffect
will run at least once. If no dependency is given in the dependency array, the useEffect
will run only one time when the component mounts.
In this example, the effect is a console.log
of the state variable count. Every time count
changes, the useEffect
is triggered because we're listening to count in our dependency array.
Now, let's understand how the cleanup function works. To do this, we need to understand the lifecycle of the useEffect
hook. Let's add another console.log for the cleanup function:
Now, when we run our code and change the count
, the component is re-rendered then the cleanup function is running and then the code in the useEffect.
useRef hook:
The useRef hook is used to track or store values without triggering a re-render. The value from useRef
will not be used in the return body. It takes an initial value and returns an object with a current property. Let’s look at an example of useRef to understand how it works. We’ll also compare it to the useState hook for better understanding.
We have here the example of the counter with the useRef hook:
When we increment the number, we will still see 0. This is because it's not causing a re-render, but in the console, we can see that the value is updated correctly:
So now we know: useRef
is a hook that provides a way to persist values across renders without causing a re-render when those values change. This makes it ideal for scenarios where you want to store information that doesn't need to affect the rendering process, such as keeping track of previous values or interacting with DOM elements directly.
Additionally, useRef
can be used to access DOM elements, making it useful for controlling focus, text selection, or animations in functional components. Since it persists through renders and doesn't cause re-renders, useRef
is efficient for certain tasks where you don't need to update the UI.
useMemo hook:
The useMemo hook is commonly used to optimize performance by memoizing the result of a computation. It acts like a cache between re-renders, ensuring that expensive calculations are only re-executed when one of the dependencies changes. This helps avoid unnecessary recalculations on every re-render, making your component more efficient.
This function simulates an expensive operation with a loop (for(let i = 0; i<= 10000000000; i++) {})
and doubles the input number. It's a heavy computation meant to illustrate performance optimization.
Every time the component re-renders, running this function would normally cause a performance delay.
useMemo(() => doubleNumber(count), [count])
: The useMemo hook memoizes (caches) the result of doubleNumber(count)
.
It only re-executes doubleNumber when the count state changes. This means if the component re-renders due to changes in the color state, the expensive doubleNumber
function won't run again unless count has changed.
We can see that when we press on Increment
the operation takes time, but the change color is immediate:
useCallback hook:
This hook is very similar to the useMemo
hook. The difference between them is that useMemo
takes a function and returns the result of that function, while useCallback
takes a function and returns the function itself.
In the next example, we can see that when the component re-renders, the getNumbers
function is recreated, even if only the color has changed. Therefore, each time the component updates, it generates a new function, even though the actual number did not change:
We can fix it with useCallback
:
useContext hook:
The useContext hook in React is used to access values from a context without needing to pass props down through multiple layers of the component tree. It simplifies prop drilling by allowing components to subscribe to context values directly.
What is a Context?
Context is a way to share data (like a global variable) between components without having to pass it explicitly via props at every level. This is useful for things like themes, user authentication, or settings, where multiple components need access to the same data.
How useContext Works:
Creating a Context:
First, you create a context usingReact.createContext()
. This creates a "context object" which holds the value you want to share.Providing the Context Value:
Next, you wrap the components that need access to the context with theContext.Provider component
. The Provider accepts a value prop, which is the data that will be available to all the components that use this context.Consuming the Context Value:
Inside a child component, instead of passing props down, you can use theuseContext
hook to access the value of the context.
In the next example, we create a context in the Example component (line 5). Then, we wrap the User component with so that any children within the User component tree can access the context. Finally, the PersonalInfo and LocationInfo components consume the context and display the data using const user = useContext(UserContext).
Result:
useReducer hook:
The useReducer
hook in React is a more advanced alternative to useState. It helps manage complex state logic in components, especially when the state depends on multiple actions or is composed of multiple values.
When you have complex state logic that involves multiple sub-values or state transitions or the next state depends on the previous state, you probably will need the useReducer
.
Let's check the example and understand from it how it works:
In line 15 we declaring on the useReducer hook it takes 2 parameters that are mandatory:
reducer
: A function that determines how the state should change, based on the action dispatched. We created the function in line 3-12.{ count : 0 }
: The starting state of your component. Usually, we use an object because we have complex variables.dispatch
: A function you call with an action to trigger the reducer function. We have thedispatch
in theincrement
anddecrement
functions.
In the reducer function, we always check the action type to determine the appropriate action. I used a switch statement, but you can also use if statements (although switch tends to be more readable).
Each time we click one of the buttons, the corresponding button's function is triggered, which calls the reducer function.
Posted on September 15, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.