React state management - useEffect & useRef
Part - 3
In the previous articles we have discussed state management and how it is achieved in react with hooks like useState, useReducer and state monitoring hooks like useMemo and useCallback in Part - 1 and Part - 2, if you want a full understanding of react state management read those articles before going ahead. In this article, we are going to look into one of the most famous hook useEffect and useRef.
useEffect
useEffect is one of the most famous hook and a hook that causes your application to break if not implemented properly. useEffect is used to perform side effects in your react components like fetching data from api, updating the state of the components and triggering rerender or implementing timers. The syntax of useEffect is useEffect(< function>, [dependencies]),
useEffect takes in two parameters one is the function and the other one is the array with dependencies in it. To understand this better let's take two examples one is fetching data and the other is implementing a timer.
Fetching data:
Code:
function NameList() {
const [names, setNames] = useState([]);
useEffect(() => {
fetch("/names.json")
.then((response) => response.json())
.then((data) => setNames(data));
},[]);
const [selectedNameDetails, setSelectedNameDetails] = useState(null);
const selectedNameChange = (name) => {
fetch(`${name}.json`)
.then( (response) => response.json())
.then((data) => setSelectedNameDetails(data));
}
return (
<div>
{names.map((name) => (
<button onClick={() =>selectedNameChange(name)}>{name}</button>
))}
{JSON.stringify(selectedNameDetails)}
</div>
)
}
In the above example, we have a vite application and a names.json file which consists of an array of names and we have created json files for each name in the array which consists of javascript objects with attributes like Id, name, and occupation. On the UI side I have displayed the names list as buttons and when you click on each button that particular name.json file will get rendered, but here's the catch whenever we are clicking the button the selected name gets changed, which will again load the entire component again, in order to stop that from happening we provide an empty dependency array to the use effect which means the component renders only once on the initial load. Here are the different scenarios of useEffect.
1. No dependency passed:
useEffect(() => {
// runs on every render
});
2. An empty array:
useEffect(() => {
// runs only on initial load
},[]);
3. Props or state values:
seEffect(() => {
//Runs on the first render
//And any time any dependency value changes
}, [prop, state]);
Implementing a timer:
Code:
function Timer(){
const [time,setTime] = useState(0);
useEffect( () => {
const interval = setInterval(() => {
setTime((t) =>{
return t + 1
});
return clearInterval(interval)
},1000)
},[]);
return(
<div>{time}</div>
)
}
Here, in this example we have implemented a timer, if you have observed the code you can notice that we have passed an arrow function to the set time where t is the parameter for the arrow function and we have used setTime inside setInterval and we stored setInterval in a variable interval. because if we have implemented the entire process directly in the useEffect the time gets stuck in the useEffect and the useffect will get triggered again and again buy if since we have implemented it inside the setInterval the noe the time will be inside the setInterval itself and gets updated but will not trigger the entire rerender of the component, understanding this is really important as this where many times we make a mistake where it causes infinite loop, go ahead and test this process inside your local system for better understanding.
useRef
useRef is a hook that is used when you don't want to render the component whenever the value gets changed. useRef Hook allows you to persist values between renders. It can be used to store a mutable value that does not cause a re-render when updated. It can be used to access a DOM element directly. useRef can be used in two types of scenarios.
1. For setting focus on a particular element when the component loads
Code:
useEffect(() => {
inputRef.current.focus();
}, []);
return (
</div>
<input type="text" ref={inputRef} />
</div>
);
}
export default App;
In this example we have an input field that refers to the variable inputRef using useRef and inside the useEffect we have set the focus, here we have used useEffect so that the focus is set to the element on the initial load and an empty dependency array so that it happens only once that is on the initial load of the component.
2. For auto assignment or auto increment
Code:
import { useRef, useEffect, useState } from "react";
function App() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
const idRef = useRef(1);
const [names, setNames] = useState([
{ id: idRef.current++, name: "John" },
{ id: idRef.current++, name: "Jane" },
]);
const onAddName = () => {
setNames([
...names,
{
id: idRef.current++,
name: inputRef.current.value,
},
]);
inputRef.current.value = "";
};
return (
<div>
<div>
{names.map((name) => (
<div key={name.name}>
{name.id} - {name.name}
</div>
))}
</div>
<input type="text" ref={inputRef} />
<button onClick={onAddName}>Add Name</button>
</div>
);
}
export default App;
In this example similar to useEffect we have used an input field to add names to the list but we want the id to be auto-incremented for that we have used the useRef and initialized it to 1 and set it to increment every time we add a name. using "inputRef.current.value",
This it for this post I hope you have understood the useEffect and useRef by reading this post, if you have liked this post please like and bookmark this blog for more posts like this.
GitHub repo for the react state management.