What is useState?
What is useState
hook? It is a feature in React that allow functional components to have states. It is called, inside a component, adding one or more local states. The hook usually consists of 2 variables.
import React, { useState } from "react";
function App() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}
export default App;
The first variable count
is the initial value. setCount
is a function that updates the initial value.
If you are confused or fairly new to React. Think of this like JavaScript.
const button = document.querySelector("button");
const countText = document.querySelector("p");
// count variable here is like the count from above
let count = 0;
// this is like the handleClick
function addValue() {
count += 1;
countText.textContent = count;
}
button.addEventListener("click", addValue);
useState with different types
You can use for all types
- String:
const [name, setName] = useState("Victoria")
- Boolean:
const [mode, setMode] = useState(false)
- Array:
const [array, setArray] = useState([])
- Object:
const [object, setObject] = useState([{ name: "Victoria", number: 123 }])
Bonus: TypeScript
In general, types should already inferred by TypeScript when:
- variable are initialized
- default values are set for parameters
- function return types are determined
When you are on the editor, hover the mouse to the array or object or any variables, there should be a popup window that says const variable: type
If type is to be determined, you should let TS know what type of this variable will be in the future.
Update or change states
Remember, arrays are mutable in JavaScript and you should treat them as immutable
when you store them in state. So avoid using push
, unshift
, pop
, shift
, splice
, reverse
, and sort
. And in React, states are considered read-only
, so you should copy the original state and then update value(s).
const array = [1, 2, 3, 4, 5];
// do this first
const copied = [...array];
// update the array
const object = { name: "Victoria", number: 123 };
// do this first
const copied = { ...object };
// update the object
Something strangeā¦? Maybe?
If you add console.log(count)
in the handleClick
function, you notice that the console will log the previous value instead of the updated value. This may not be a dealbreaker for certain cases.
Letās say you want to have a button that adds 1 and another button that adds 3.
function App() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
// function handleClick() {
// setCount((prev) => prev + 1);
// }
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>add 1</button>
<button
onClick={() => {
handleClick();
handleClick();
handleClick();
}}
>
add 3
</button>
</div>
);
}
Letās see the actions below!
Notice, it only adds 1 after clicked add 3
with setCount(count + 1)
.
Notice, it adds 3 after clicked add 3
with setCount(prev => prev + 1)
Note: I should name the parameter better, like prevCount.
According to the docs, it is called Updater
function (I didnāt know the name for this). It takes the previous count 5 based on the GIF example and then return next value, which is 8.
Do you need to always write updater function? Maybe? But, I would say depend on the case, if you need to write updater in setState, then go ahead. I do not know which one is the best/better practice. I personally donāt see any harms to write setState
with or without the updater function.
Personal story with updater function
When I was working on the rest-countries-api (the JS version), I was so confused why the pagination was not behaving the way I wanted it to be. At the time, I was not thinking about the updater function. So, I had to adjust the math with totalPage
to make it behaves the way I wanted.
The JS version:
const handleNext = (page) => {
const FIVE = displayPages.length;
const condition = page <= Math.floor(FIVE / 2);
if (condition) {
setCurrentPage(page + 1);
setStartPage(0);
setEndPage(5);
} else {
setCurrentPage(page >= totalPages ? totalPages : page + 1);
// notice the totalPages - 2?? weird, right?
setStartPage(page >= totalPages - 2 ? totalPages - 5 : startPage + 1);
setEndPage(page >= totalPages ? totalPages : endPage + 1);
}
};
If I have the totalPage -5
instead of the magic number 2
, the GIF shows how it behaves.
Later, I decided to re-built the same app with TypeScript. While I was working on the app, I realized that I could have implement with the updater function. The math logic will sound right.
The TS version:
Note: prev in this case is the prevPage.
const forward = () => {
const FIVE = displayPages.length;
const condition = currentPage <= Math.floor(FIVE / 2);
if (condition) {
setCurrentPage((prev) => prev + 1);
setStart(0);
setEnd(5);
} else {
setCurrentPage((prev) => (prev >= total ? total : prev + 1));
// with updater function, I can write total - 5 and this makes sense to me
setStart((prev) => (prev >= total - 5 ? total - 5 : start + 1));
setEnd((prev) => (prev >= total ? total : end + 1));
}
};
This time, it works flawlessly!
Keep the updater function in your mind. If a state is behaving strangely, you may need to write it!
Resources
Thank you!
Thank you for your time and for reading this!