A Glance at State Hooks in React
Hooks vs. Class Components
Hooks make your react code look simpler. Take a counter program as an example.
Using class components:
import React from "react";
class ClassComponent extends React.Component {
constructor() {
super();
this.state = {
count: 0
};
this.increase = this.increase.bind(this);
}
increase() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.increase}>+</button>
</div>
);
}
}
export default ClassComponent;
But with hooks, one can import useState
first and instead of creating a class.
import React, { useState } from "react";
function FunctionalComponent() {
const [count, setCount] = useState(0);
function increase() {
setCount(count + 1);
}
return (
<div>
<h1>{count}</h1>
<button onClick={increase}>+</button>
</div>
);
}
export default FunctionalComponent;
Hooks provide a more direct API to the React concepts like props, state, context, refs, and lifecycle. There are a variety of hooks available: 1. that can store data (e.g. useState
,useReducer
) and connect data with lifecycle; 2. that can provide some memory functionality (e.g. useMemo
,useCallback
) so we can store some values in the previous execution; 3. that can provide more control (e.g. useEffect
,useRef
, useContext
) so we have a extensive access and control inside React.
Example: Clock with State Hooks
Here is an example using state hooks to update a clock every one second. with setInterval(updateTime, 1000);
. The const [time, setTime]
line follows the JavaScript syntax called array destructuring. One can think the time
as this.state.time
and setTime
as this.setState
in a class.
import React, { useState } from "react";
function App() {
setInterval(updateTime, 1000);
const [time, setTime] = useState(new Date().toLocaleTimeString("en-GB"));
function updateTime() {
const newTime = new Date().toLocaleTimeString("en-GB");
setTime(newTime);
}
return (
<div className="container">
<h1>{time}</h1>
<button onClick={updateTime}>Get Time</button>
</div>
);
}
export default App;
Example: To-Do List using State Hooks and Props
Below is a to-do list (source code: codesandbox). The App
component (shown below) contains two components - InputArea
and ToDoItem
. items
is the array used to store items showing on the screen, setItems
is the function we use to update the items
. Since we just want to add / delete an item from items
but not to create a new array every time, we can work on the previous state with React hook and use spread operator in JS ES6 to extend the array or use filter
method to delete the item that is clicked.
import React, { useState } from "react";
import ToDoItem from "./ToDoItem";
import InputArea from "./InputArea";
function App() {
const [items, setItems] = useState([]);
function addItem(inputText) {
setItems((prevItems) => {
return [...prevItems, inputText];
});
}
function deleteItem(id) {
setItems((prevItems) => {
return prevItems.filter((item, index) => {
return index !== id;
});
});
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<InputArea onAdd={addItem} />
<div>
<ul>
{items.map((todoItem, index) => (
<ToDoItem
key={index}
id={index}
text={todoItem}
onChecked={deleteItem}
/>
))}
</ul>
</div>
</div>
);
}
export default App;
InputArea
(shown below) contains inputText
which stores the text typed in the input. It is updated by the onChange
‘s handleChange
in the input
element. What interesting is the onClick
inside the button
element. It allows us to add what we just typed into the array defined in App.jsx
. If we take a closer look, the onAdd
which carries addItem
is passed from App.jsx
(Yes! We can pass functions as props). Since we don’t want the addItem
executed immediately when the page gets rendered, we write line 14-16 using the arrow operator.
import React, { useState } from "react";
function InputArea(props) {
const [inputText, setInputText] = useState("");
function handleChange(event) {
const newValue = event.target.value;
setInputText(newValue);
}
return (
<div className="form">
<input onChange={handleChange} type="text" value={inputText} />
<button
onClick={() => {
props.onAdd(inputText);
setInputText("");
}}
>
<span>Add</span>
</button>
</div>
);
}
export default InputArea;
Similarly, in ToDoItem.jsx
(shown below) the props.onChecked
was passed from App.jsx
and it’s the function deleteItem
. Since props.onChecked
is called from ToDoItem.jsx
, we also need to pass the id
from App.jsx
to ToDoItem.jsx
using props so we can delete that item properly.
import React from "react";
function ToDoItem(props) {
return (
<div
onClick={() => {
props.onChecked(props.id);
}}
>
<li>{props.text}</li>
</div>
);
}
export default ToDoItem;