Table of contents
What are Hooks? 🙄
Hooks let developers use state and other React features without writing a class. Hooks were introduced in React 16.8 to facilitate programmers in the reusability of React code.
They are the functions that "hook into" React state and lifecycle features from function components. It does not work inside classes.
When would I use a Hook?
If writing a function component and realizing the need to add some state to it, previously we had to convert it to a class. Now we can use a Hook inside the existing function component.
Rules of Hooks:
- Hooks should only be called from the top level of your React function
- Hooks must not be called from nested code (e.g., loops, conditions).
- Hooks may also be called at the top level from custom Hooks.
Now let’s look at the two hooks which are most confused between 😬
useState Hook: 😍
Definition:
useState hook is the primary building block that enables functional components to hold state between re-renders. It enables the development of the component state for functional components.
Usage: 🙃
import React, { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={() => setCount(count + 1)}>Count is :{count}</button>
</div>
);
}
Above is the code for functional implementation of React useState
and below is the equivalent CodeSandbox Output .
https://codesandbox.io/embed/priceless-fast-hijeoo?fontsize=14&hidenavigation=1&theme=dark
Let’s see the same code using class components-based method.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
We see how much the code is reduced significantly and using functional components and hook also makes code much clearer by not using this keyword and eliminating a wrapper constructor within class
component.
What does useState do? 🤔
First of all, it declares ‘state variables’. It is used to preserve value between the function calls. Normally, common Javascript variables like ( let
, var
, const
) disappear after the function call. However, state variables preserve the values.
Explaining the Syntax:
[count,setCount]=useState(0)
count
is the state variable that will be used to represent the value assigned using the hook.setCount
is a functional variable whose sole purpose is updatingcount
state variable.- Within brackets, after
useState
initial argument is passed which is used for the initial value of the count after initial rendering. - Square Brackets mean that when we declare a state variable with
useState
it returns an array with two items. The first one is the current value and the second is a function that updates it.
React will remember its current value between re-renders, and provide the most recent one to our function. If we want to update the current
count
, we can callsetCount
When the App component re-renders, its children would re-render.
Gotcha! 😳
const [localState, setLocalState] = useState(props.theme);
useRef Hook: 😍
Definition:
The useRef Hook allows persisting 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.
It does everything that useState does but without re-rendering the components.
useRef
returns a mutable ref object whose .current
property is initialized to the passed argument (initialValue
). The returned object will persist for the full lifetime of the component.
The major question that arises is why use useRef if we have useState? 🤔
- Easier to work with as updates are synchronous.
- Do not trigger re-render. Performance optimization.
- Prevents render thrashing
- Can be used if we want the state to presist value throughout the component lifecycle.
- If we want to access the DOM element directly
useRef
should be used.
Usage: 😜
import { useState, useRef } from "react";
import "./styles.css";
export default function AppDemo11() {
const [value, setValue] = useState("");
const valueRef = useRef();
console.log("render");
const handleClick = () => {
console.log(valueRef);
setValue(valueRef.current.value);
};
return (
<div className="App">
<h4>Value: {value}</h4>
<input ref={valueRef} />
<button onClick={handleClick}>click</button>
</div>
);
}
We see the code used for useRef
and equivalent output in CodeSandbox.
https://codesandbox.io/embed/cranky-ellis-1s6jrt?fontsize=14&hidenavigation=1&theme=dark
We see that component only renders after button click function rather than rendering on each keystroke.
When we “submit” the input with a button to update the state variable value
.With the ref
property, React provides direct access to React components or HTML elements.
<input ref={valueRef} />
If this had been done using useState
and onChange
which each keystroke input the component would have re-rendered. With each keystroke, component is re-rendered.
We see that in effect in the below code and it’s output in CodeSandBox.
import { useState, useRef, useEffect } from "react";
import "./styles.css";
export default function AppDemo11() {
const [value, setValue] = useState("");
const rendercount = useRef(0);
useEffect(() => {
rendercount.current = rendercount.current + 1;
console.log(rendercount.current);
});
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<div className="App">
<input value={value} onChange={handleChange} />
<h4>Renders:{rendercount.current} </h4>
</div>
);
}
(codesandbox.io/embed/competent-cerf-6e3vp0?..)
However, using useRef
makes sure that the component is rendered only once after the button is clicked.
Explaining the Syntax:
const valueRef = useRef(null);
const onButtonClick = () => {
console.log(valueRef.current.value);
};
valueRef
is the variable that stores the value to be persisted.- Within the brackets, after the
useRef
statement, it is provided the initial value which should be assigned to useRef on the first render. - We see that within the Button click function we access value within
valueRef
using .current
keyword. This is becauseuseRef
is like a box that holds its mutable value in its.current
property.
Keep in mind that
useRef
doesn’tnotify when its content changes. Mutating the.current
property doesn’t cause a re-render.
Gotcha! 😳
Examples of different use cases for useState vs useRef: 🤠
We have already seen two major use cases for useRef over useState
- Accessing DOM input using ref directly.
- Persisting values of mutable variables and not causing re-renders on updation.
Let’s see some more use cases of these points.
- In the first example, we simply try counting the no of renders using
useRef
anduseState
.
useState code:
import { useState, useRef, useEffect } from "react";
import "./styles.css";
export default function AppDemo11() {
const [value, setValue] = useState("");
const [rendercount,setrenderCount] = useState(0);
useEffect(() => {
setrenderCount(rendercount+ 1);
});
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<div className="App">
<input value={value} onChange={handleChange} />
<h4>Renders:{renderco} </h4>
</div>
);
}
This above code will cause infinite renders as every time rendercount
value is updated the entire component is re-rendered.
useRef code:
Equivalent use case can be achieved using useRef
whererin within useEffect
hook ( important to use within useEffect
to avoid side effects) rendercount.current
value is updated which doesn’t trigger any re rendering.
import { useState, useRef, useEffect } from "react";
import "./styles.css";
export default function AppDemo11() {
const [value, setValue] = useState("");
const rendercount = useRef(0);
useEffect(() => {
rendercount.current = rendercount.current + 1;
console.log(rendercount.current);
});
const handleChange = (e) => {
setValue(e.target.value);
};
return (
<div className="App">
<input value={value} onChange={handleChange} />
<h4>Renders:{rendercount.current} </h4>
</div>
);
}
- In the second example Let’s take a second use case of creating a timer using
useState
anduseRef
.
The code is for printing “A second has passed” in console for each passing second
useState code:
We see in this code that it goes into an infinite loop and keeps on re-rendering.
As with each update of state variable intervalUse
component is re-rendered. Code goes into infinite loop hell.
import "./styles.css";
import React, { useRef, useEffect, useState } from "react";
export default function App() {
const [intervalUse,setintevalUse]= useState()
useEffect(() => {
const id = setInterval(() => {
console.log("A second has passed");
}, 1000);
setintevalUse(id)
});
return (
<div>
</div>
);
}
useRef code:
So to prevent inifinte loop hell we use useRef
.
With each passing second “A second has passed” is printed on the console and state variable val
is updated which is rendered on the screen.
import "./styles.css";
import React, { useRef, useEffect, useState } from "react";
export default function App() {
const[val,setVal]=useState(0)
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
setVal(val+1)
console.log("A second has passed");
}, 1000);
intervalRef.current = id;
return () => clearInterval(intervalRef.current);
});
const handleCancel = () => clearInterval(intervalRef.current);
return (
<div>
<div>Value is {val}</div>
</div>
);
}
See the ouput in CodeSandBox. Using useRef
makes sure that there is no case for inifinite rendering.
https://codesandbox.io/embed/upbeat-ganguly-nbg47c?fontsize=14&hidenavigation=1&theme=dark
Finally all the Differences in Brief: 😌
- Both preserve their data during render cycles and UI updates, but only the
useState
Hook with its updater function causes re-renders. useRef
returns an object with acurrent
property holding the actual value. In contrast,useState
returns an array with two elements: the first item constitutes the state, and the second item represents the state updater functioncurrent
property in useRef is mutable howeveruseState
state variable is not. We need updater function to update useState state variable.useState
anduseRef
both are Hooks, but onlyuseRef
can be used to gain direct access to React components or DOM elements.
Concluding: 🤩
It should be clear by now that useState
is to be used if we want to update data and cause a UI update.
And useRef
is to be used if data is to be persisted throughout the lifecycle without re-renders.