React Hooks are a Game changer

May 03, 2020 9 min read

Why Hooks

In 2018, Hooks were announced as part of React 16.8 as a way to manage state inside functional components without requiring the use of an ES6 JavaScript class.

Hooks mark a major departure from traditional class-based React components.

Functional components are smaller in lines of code and quicker to compose. They are also more approachable, because they don’t require you to understand the nuances of ES6 classes or memorize lifecycle method names.

So instead of bemoaning the complexity of React’s class-based components, I found myself falling in love with writing small, functional components in React.

With Hooks at our disposal, the cost of creating new components is lowered significantly. We can start writing a component, pull in other components on a whim (powered by smart IDE auto-imports), and refactor a single component into multiple components in the same file.

Custom Hooks

1. State and useState

State allows components to create and manage their own data. So unlike props, components cannot pass data with state, but they can create and manage it internally.

class Test extends React.Component {
constructor() {
this.state = {
id: 1,
name: "test"
};
}
render() {
return (
<div>
<p>{this.state.id}</p>
<p>{this.state.name}</p>
</div>
);
}
}

By importing useState in your code you’re signaling the intent to hold some kind of state inside your React component.

import React {useState}from 'react';
// create state variable
// syntax: const [stateVariable] = React.useState(defaultValue);
function App() {
const [language] = React.useState('javascript');
// we use array destructuring to declare state variable
return <div>I am learning {language}</div>;
}

what happens when state changes

A change in the state happens based on user-input or triggering an event. Also, React components (with state) are rendered based on the data in the state. State holds the initial information.

So when state changes, React gets informed and immediately re-renders the DOM – not the whole DOM, but only the component with the updated state. This is one of the reasons why React is fast.

2. Side effects and useEffect

useEffect lets us perform side effects in function components.

Side effects- are where we need to reach into the outside world. For example, fetching data from an API or working with the DOM.

Side effects are actions that can change our component state in an unpredictable fashion (that have cause ‘side effects’).

useEffect accepts a callback function (called the ‘effect’ function), which will by default run every time there is a re-render

useEffect runs once our component mounts, which is the right time to perform a side effect in the component lifecycle

// This code picks a color from the colors array
// and makes it the background color
function App() {
const [colorIndex, setColorIndex] = React.useState(0);
const colors = ["blue", "green", "red", "orange"];
// we are performing a 'side effect' since we are working with an API
// we are working with the DOM, a browser API outside of React
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
});
// whenever state is updated, App re-renders and useEffect runs
function handleChangeIndex() {
const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
setColorIndex(next);
}
return <button onClick={handleChangeIndex}>Change background color</button>;
}

To avoid executing the effect callback after each render, we provide a second argument, an empty array

function App() {
...
// now our button doesn't work no matter how many times we click it...
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
}, []);
// the background color is only set once, upon mount
// how do we not have the effect function run for every state update...
// but still have it work whenever the button is clicked?
return (
<button onClick={handleChangeIndex}>
Change background color
</button>
);
}

useEffect lets us conditionally perform effects with the dependencies array The dependencies array is the second argument and if any one of the values in the array changes, the effect function runs again

function App() {
const [colorIndex, setColorIndex] = React.useState(0);
const colors = ["blue", "green", "red", "orange"];
// we add colorIndex to our dependencies array
// when colorIndex changes, useEffect will execute the effect fn again
useEffect(() => {
document.body.style.backgroundColor = colors[colorIndex];
// when we use useEffect, we must think about what state values
// we want our side effect to sync with
}, [colorIndex]);
function handleChangeIndex() {
const next = colorIndex + 1 === colors.length ? 0 : colorIndex + 1;
setColorIndex(next);
}
return <button onClick={handleChangeIndex}>Change background color</button>;
}
  • Fetching data with useEffect

    Note that handling promises with the more concise async/await syntax requires creating a separate function (Why? The effect callback function cannot be async)

const endpoint = "https://api.github.com/users/codeartistryio";
// with promises:
function App() {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
// promises work in callback
fetch(endpoint)
.then(response => response.json())
.then(data => setUser(data));
}, []);
}
// with async / await syntax for promise:
function App() {
const [user, setUser] = React.useState(null);
// cannot make useEffect callback function async
React.useEffect(() => {
getUser();
}, []);
// instead, use async / await in separate function, then call
// function back in useEffect
async function getUser() {
const response = await fetch("https://api.github.com/codeartistryio");
const data = await response.json();
setUser(data);
}
}

3.Performance and useCallback

useCallback is a hook that is used for improving our component performance If you have a component that re-renders frequently, useCallback prevents callback functions within the component from being recreated every single time the component re-renders (which mean the function component re-runs) useCallback re-runs only when one of it’s dependencies changes

// in Timer, we are calculating the date and putting it in state a lot
// this results in a re-render for every state update
// we had a function handleIncrementCount to increment the state 'count'...
function Timer() {
const [time, setTime] = React.useState();
const [count, setCount] = React.useState(0);
// ... but unless we wrap it in useCallback, the function is
// recreated for every single re-render (bad performance hit)
// useCallback hook returns a callback that isn't recreated every time
const inc = React.useCallback(
function handleIncrementCount() {
setCount(prevCount => prevCount + 1);
},
// useCallback accepts a second arg of a dependencies array like useEffect
// useCallback will only run if any dependency changes (here it's 'setCount')
[setCount]
);
React.useEffect(() => {
const timeout = setTimeout(() => {
const currentTime = JSON.stringify(new Date(Date.now()));
setTime(currentTime);
}, 300);
return () => {
clearTimeout(timeout);
};
}, [time]);
return (
<div>
<p>The current time is: {time}</p>
<p>Count: {count}</p>
<button onClick={inc}>+</button>
</div>
);
}

4. Memoization and useMemo

useMemo is very similar to useCallback and is for improving performance, but instead of being for callbacks, it is for storing the results of expensive calculations useMemo allows us to ‘memoize’, or remember the result of expensive calculations when they have already been made for certain inputs (we already did it once for these values, so no need to do it again) useMemo returns a value from the computation, not a callback function (but can be a function)

// useMemo is useful when we need a lot of computing resources
// to perform an operation, but don't want to repeat it on each re-render
function App() {
// state to select a word in 'words' array below
const [wordIndex, setWordIndex] = useState(0);
// state for counter
const [count, setCount] = useState(0);
// words we'll use to calculate letter count
const words = ["i", "am", "learning", "react"];
const word = words[wordIndex];
function getLetterCount(word) {
// we mimic expensive calculation with a very long (unnecessary) loop
let i = 0;
while (i < 1000000) i++;
return word.length;
}
// Memoize expensive function to return previous value if input was the same
// only perform calculation if new word without a cached value
const letterCount = React.useMemo(() => getLetterCount(word), [word]);
// if calculation was done without useMemo, like so:
// const letterCount = getLetterCount(word);
// there would be a delay in updating the counter
// we would have to wait for the expensive function to finish
function handleChangeIndex() {
// flip from one word in the array to the next
const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1;
setWordIndex(next);
}
return (
<div>
<p>
{word} has {letterCount} letters
</p>
<button onClick={handleChangeIndex}>Next word</button>
<p>Counter: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}

5. Refs and useRef

Refs are a special attribute that are available on all React components. They allow us to create a reference to a given element / component when the component mounts. useRef allows us to easily use React refs

  • We call useRef (at top of component) and attach the returned value to the element’s ref attribute to refer to it
    • Once we create a reference, we use the current property to modify (mutate) the element’s properties or can call any available methods on that element (like .focus() to focus an input)
function App() {
const [query, setQuery] = React.useState("react hooks");
// we can pass useRef a default value
// we don't need it here, so we pass in null to ref an empty object
const searchInput = useRef(null);
function handleClearSearch() {
// current references the text input once App mounts
searchInput.current.value = "";
// useRef can store basically any value in its .current property
searchInput.current.focus();
}
return (
<form>
<input
type="text"
onChange={event => setQuery(event.target.value)}
ref={searchInput}
/>
<button type="submit">Search</button>
<button type="button" onClick={handleClearSearch}>
Clear
</button>
</form>
);
}

There are many other React concepts to learn, but these are the ones I believe you must know before any others to set you on the path to React mastery in 2020.


 What I learnt from creating my siteArray Data Structures in Javascript 

Want more?

Subscribe to get my latest content via email. I won’t send you spam, and you can unsubscribe at any time.