React 101: Hooks — Part 1

Hooks are important in React as they help to make interactive and dynamic web applications.
Previously in React 101 series, I have written about managing static applications. To make an application interactive, React components need variables of their own. These variables (state) are managed by React Hooks. Using Hooks, we can determine what we want to show the users by declaring how our user interface should look based on the state.
React offers a number of built-in Hooks. A few of these include useState()
, useEffect()
, useContext()
, useReducer()
, and useRef()
. See the full list in the React docs.
Props v/s State
“Props” refers to the properties being passed into a component in order for it to work correctly, similar to how a function receives parameters. A component receiving props is not allowed to modify those props i.e. props are immutable.
“State” refers to values that are defined within the component and managed by it, similar to variables declared inside a function. Any time you have changing values that should be saved/displayed, you will likely be using state. The state is mutable.
When would you want to use props instead of state?
Anytime you want to pass data into a component so that component can determine what will get displayed on the screen.
When would you want to use state instead of props?
Anytime you want a component to maintain some values from within the component and “remember” those values even when React re-renders the component.
State Hook

A State Hook is the most common Hook used for building React components. It is a named export from the React library, and is imported like this:
import React, {useState} from 'react'
If we console.log(useState())
and console.log(useState("Hello"))
, we get an array back with two values.
[undefined, f()]
//and
["Hello", f()]
- The current state: The current value of this state. The first time it was undefined and the second time a string was passed.
- The state setter: A function that we can use to update the value of this state.
These two values can be used to track the current state of a data value and change it as needed. The array values can be stored in local variables using array destructuring.
//Destructing
const [currentState, setCurrentState] = useState("Hello")
console.log(currentState) // Hello
Changing State
The value of the state can be changed using the setter function returned by calling useState
:
import React, { useState } from "react";
function Toggle() {
const [toggle, setToggle] = useState();
return (
<div>
<p>The toggle is {toggle}</p>
<button onClick={() => setToggle("On")}>On</button>
<button onClick={() => setToggle("Off")}>Off</button>
</div>
);
}
The setter function setToggle
is called by onClick
event listeners. The value of toggle
variable is updated and the component is re-rendered with the new state value.
With the State Hook, updating the state is as simple as calling a state setter function. Calling the state setter signals to React that the component needs to re-render, so the whole function defining the component is called again.
The magic of useState()
is that it allows React to keep track of the current value of the state from one render to the next!
Initialize State
A State Hook can be used to manage the value of any primitive data type and even data collections like arrays and objects.
In the following snippet, no initial value is passed, so the value of isLoading
variable will be undefined if you log on the console:
import React, { useState } from 'react';
function ToggleLoading() {
const [isLoading, setIsLoading] = useState();
return (
<div>
<p>The data is {isLoading ? 'Loading' : 'Not Loading'}</p>
<button onClick={() => setIsLoading(true)}>
Turn Loading On
</button>
<button onClick={() => setIsLoading(false)}>
Turn Loading Off
</button>
</div>
);
}
A value is passed as an argument to useState()
function call to initialize the value of the state.
const [isLoading, setIsLoading] = useState(true);
- During the first render, the initial state argument is used.
- When the state setter is called, React ignores the initial state argument and uses the new value.
- When the component re-renders for any other reason, React continues to use the same value from the previous render.
Note: if you ever need the old value of state to help you determine the new value of state, you should pass a callback function to your state setter function instead of using the state directly. This callback function will receive the old value of state as its parameter, which you can then use to determine your new value of state.
Whenever you don’t need the previous value of the state to determine what the new value of the state should be, you can set the new value as it's done in the previous example.
Set from Previous State
React state updates are asynchronous. Directly making changes to the current state is not recommended. It is a best practice to update a state with a callback function, preventing accidentally outdated values.
In the following code, when the button is pressed, the increment()
event handler is called. Inside this function, setCount()
, state setter function is used with a callback function.
The callback function gives access to the previous value that can be updated, instead of directly updating the current state — count
variable.
import React, { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(prevCount => prevCount + 1);
return (
<div>
<p>Wow, you've clicked that button: {count} times</p>
<button onClick={increment}>Click here!</button>
</div>
);
}
Code Snippet for a QuizNavBar
A small quiz application to practice the use of State Hook.
Creating a state for quiz questions:
const [questionIndex, setQuestionIndex] = useState(0);
Quiz event handlers, so that they call the setter function of the state.
// define event handlers
const goBack = () => setQuestionIndex(prevQuestionIndex => prevQuestionIndex -1);
const goToNext = () => setQuestionIndex(prevQuestionIndex => prevQuestionIndex + 1);
Adding Event Handlers to the Navbar Component.
As JavaScript Arrays use zero-based indexing, the questionIndex
is added 1 to number it correctly or the initial state value can be set to 1.
return (
<nav>
<span>Question #{questionIndex + 1}</span>
<div>
<button onClick={goBack}>
Go Back
</button>
<button onClick={goToNext}>
Next Question
</button>
</div>
</nav>
);
To determine the first and last questions to disable click on the buttons:
const onFirstQuestion = questionIndex === 0
const onLastQuestion = questionIndex === questions.length - 1;
The complete application using State Hook will be:
mport React, { useState } from 'react';
export default function QuizNavBar({ questions }) {
const [questionIndex, setQuestionIndex] = useState(0);
// define event handlers
const goBack = () => setQuestionIndex(prevQuestionIndex => prevQuestionIndex -1);
const goToNext = () => setQuestionIndex(prevQuestionIndex => prevQuestionIndex + 1);
// determine if on the first question or not
const onFirstQuestion = questionIndex === 0
const onLastQuestion = questionIndex === questions.length - 1;
return (
<nav>
<span>Question #{questionIndex + 1}</span>
<div>
<button onClick={goBack} disabled={onFirstQuestion}>
Go Back
</button>
<button onClick={goToNext} disabled={onLastQuestion}>
Next Question
</button>
</div>
</nav>
);
}
In the next topic, I will write about Arrays and Objects in State, and how to pass them to other components. Stay Tuned!