Mastering React Hooks: Essential Insights on useState & useEffect
Written on
Chapter 1: Introduction to React Hooks
As developers, we embark on exciting journeys harnessing the capabilities of useState and useEffect to energize our applications. Yet, amidst this thrill, there are hidden traps that can catch the unprepared.
Worry not! This guide will lead you through these challenging terrains, providing you with the insights and tools necessary for safe navigation. Letβs dive in and demystify useState and useEffect together! π
Section 1.1: Understanding useState
const [state, setState] = useState(initialState);
This hook returns a state value and a function for updating it. Initially, the state (state) holds the same value as the provided first argument (initialState). The setState function is responsible for updating the state and triggering a re-render of the component.
For example:
const [count, setCount] = useState(0);
function handleOnClick() {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}
If you click the button three times consecutively, you will notice that the displayed count only increments by +1 instead of +3. π
Functional Updates
If the new state needs to be derived from the previous state, you can pass a function to setState:
setCount(count => count + 1);
setCount(count => count + 1);
setCount(count => count + 1);
Updating Objects
When using useState with objects, you might encounter cases where the component view does not update:
const [list, setList] = useState([0, 1, 2]);
const [userInfo, setUserInfo] = useState({
name: 'Bob',
age: 20
});
function handleOnClick() {
list.push(4);
setList(list);
userInfo.name = 'Jack';
userInfo.age = 30;
setUserInfo(userInfo);
}
Cause of the Issue: The problem arises from React's shallow comparison mechanism. When the state is an object, React keeps a reference to that object. If the reference remains unchanged, React assumes no updates are needed and skips re-rendering. π
Solution: Change the reference of the original object. This can be done by cloning the object using the ES6 spread operator or various array methods:
const nextList = list.slice(0);
nextList.push("slice");
setList(nextList);
Summary
It's crucial to avoid directly manipulating the object itself when using useState. Always clone the object before making any changes to prevent unexpected issues.
Section 1.2: Handling State Updates
Since setState does not update immediately, React batches multiple setState calls before applying changes. As such, fetching the latest value after a state update can be tricky. Here are some methods to manage this:
- Using useRef: This method allows you to store the value of useState without triggering re-renders.
- Using useEffect: This approach is effective but might not always be optimal, as it runs on every update, regardless of necessity.
- Functional Updates.
- Using a custom hook like useGetState: This can help in accessing the latest state reliably.
const useGetState = (initialState) => {
const [state, setState] = useState(initialState);
const stateRef = useRef(state);
stateRef.current = state;
const getState = useCallback(() => stateRef.current, []);
return [state, setState, getState];
};
Getting the Latest Value from a Timer
In the following scenario, the count remains 0 regardless of interaction:
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1);}, 1000);
return () => {
clearInterval(interval);};
}, []);
Cause of the Problem: The timer does not clear upon creation, causing the internal state to always reflect its initial value.
Solutions:
- Update the state using functional updates.
- Include the state as a dependency in useEffect to ensure the timer is recreated upon state changes. π
useEffect(() => {
console.log("useEffect", count);
}, [count]);
Chapter 2: Exploring useEffect
The first video titled "All 12 useState & useEffect Mistakes Junior React Developers Still Make in 2024" addresses common errors made by novice developers.
The second video titled "Learn useState in 8 Minutes - React Hooks Explained (2023)" provides a concise overview of the useState hook.
Lifecycle of useEffect
If you're familiar with class component lifecycle methods, you can think of useEffect as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount.
Unlike componentDidMount, the function passed to useEffect runs after the browser completes rendering. This feature makes it ideal for handling side effects like subscriptions and event listeners. π
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = You clicked ${count} times;});
return (
<button onClick={() => setCount(count + 1)}>
Click me</button>
);
}
Controlling Execution with Dependencies
You can manage when useEffect runs based on dependencies.
useEffect(() => {
console.log("Updated count:", count);
}, [count]);
#### Common Pitfalls with Objects as Dependencies
When using objects as dependencies in useEffect, you may encounter unexpected behaviors.
For instance, modifying an object directly may not trigger useEffect:
const [info, setInfo] = useState({ name: "Bob", age: 20 });
useEffect(() => {
console.log("info", info);
}, [info]);
function handleChangeName(e) {
const value = e.target.value;
setInfo((info) => {
info.name = value; // This will not trigger useEffect
return info;
});
}
Correct Approach: Always return a new object to ensure React recognizes changes:
setInfo({ ...info, name: value });
Conclusion
In the expansive realm of React development, useState and useEffect serve as powerful tools, capable of enhancing our applications significantly. However, they also come with their own challenges and intricacies.
This guide has equipped you with the knowledge required to navigate these complexities safely. As you continue your development journey, stay vigilant, test thoroughly, and don't hesitate to seek assistance from your peers or trusted resources. With diligence and a solid understanding of useState and useEffect, you can tackle even the toughest React challenges and emerge successful. Happy coding! ππ