React Hooks

Before diving in it is important to know the difference between object oriented programming (OOP) and functional programming (FP). They both have their pros and cons depending on the use case. Typically for building complex systems that have well defined predetermined structure OOP is the way to go as it allows for a more efficient use of resources. However for a single page react application for example with a few components and maybe a few apis functional programming, will probably be quicker and easier to dive right in. So Hooks, unlike class based react components are built using functional programming principles in mind.

With that in mind let us walk you through a few of the hooks i have started using and would like to share. I will skip useState for now.

useEffect

This is a powerful function as it replaces multiple lifecycle methods in react namely (componentDidMount, componentDidUpdate, and componentWillUnmount).

This hooks comes in two forms. With or Without Cleanup. For the latter it takes an optional cleanup method as a return value so that we can implement asynchronous functions like a timer or subscription based apis.

It is also important to note that each render cycle will recreate the effect so you can think of it belonging to a particular render cycle.

A few options about the second argument:

Don’t provide it => (re-renders based on state updates)

Empty array => (renders only once)

Array of variable/s => (renders once each time an element in the dependencies array changes between renders)

Three simple use cases of the effect hook: Data fetching, Setting up a subscription and DOM Manipulation. Let us look at an example for each of these use cases.

Data Fetching:

This is is a very clean way to asynchronously load data from a third party api while your page loads.

useEffect(() => {  /*
Per Hooks documentation:
Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.. */ const fetchData = async () => { const result = await axios("data_fetch_url..."); setData(result.data); }; fetchData();}, []);

Subscription (Requires cleanup to avoid memory leaks):

Cleanup is handled in the return callback function.

useEffect(() => {  const interval = setTimeout(() => {    setLoader...  }, 5000);  return () => {    clearInterval(interval);  };});

DOM Manipulation (Through useRef):

Here we tag a paragraph element and update its text every time the state variable count changes. We are also setting the image’s source attribute through the useEffect hook on component render.

import React, { useState, useEffect, useRef } from "react";
import { loadProgressBar } from "axios-progress-bar";
import axios from "axios";
const url =
"https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg";
function DomManipulator() {
const [count, setCount] = useState(0);
const counterRef = useRef(null);
const handleClicker = newCount => {
newCount === 6
? console.log("Maximum number of clicks reached")
: setCount(newCount);
};
useEffect(() => {
console.log('counter ref: ', counterRef)
counterRef.current.innerText = `You clicked ${count} times`;
}, [count]); // count update controls the render cycles
useEffect(() => {
loadProgressBar();
axios
.get(url, { responseType: "blob" })
.then(response => {
const reader = new window.FileReader();
reader.readAsDataURL(response.data);
reader.onload = () => {
document.getElementById("img").setAttribute("src", reader.result);
};
})
.catch(error => {
console.log(error);
});
}, []); // Render once on component mount
return (
<div>
<p ref={counterRef}>You clicked 0 times</p>
<button onClick={() => handleClicker(count + 1)}>Click me</button>
<button onClick={() => setCount(0)}>Reset</button>
<br />
<p className="imageLoad">
<img id="img" alt="sunset" />
</p>
</div>
);
}
export default DomManipulator;

useReducer

Similar to useState but typically used to share state across components

Basic use case: shopping list

import React, { useReducer, useRef } from "react";
import "./styles.css";
const ShoppingList = () => {
const inputRef = useRef();
const [items, dispatch] = useReducer(
(state, action) => {
switch (action.type) {
case "add":
return [
...state,
{
id: state.length,
name: action.name
}
];
case "remove":
// keep every item except the one we want to remove
return state.filter((_, index) => index !== action.index);
case "reset":
return [];
default:
return state;
}
},
[{ name: "flour" }]
);
const handleSubmit = e => {
e.preventDefault();
dispatch({
type: "add",
name: inputRef.current.value
});
inputRef.current.value = "";
};
return (
<>
<form onSubmit={handleSubmit}>
<input ref={inputRef} />
</form>
<ul>
{items.map((item, index) => (
<li key={item.id}>
{item.name}{" "}
<button onClick={() => dispatch({ type: "remove", index })}>
X
</button>
</li>
))}
</ul>
<button onClick={() => dispatch({ type: "reset" })}>Clear All</button>
</>
);
};
export default ShoppingList;

So as seen here useReducer provides a dispatch api to update the state and you could submit an update from different parts of the application.

useContext

This is a powerful hooks when combined with useReducer. I have written an article specifically for this which you can view here