Refactoring a Class Component to a Functional Component, with Hooks
Hooks were introduced a little more than two years ago, when Facebook released React 16.8 in February 2019. Shifting over to hooks can be a little confusing, especially for developers who’ve gotten used to class components. Once you’ve made the adjustment, however, there’s no looking back.
Here’s what an incredibly barebones class component with state and a fetch call might look like.
Class Component
import './App.css';
import React, {Component} from 'react';
import CardContainer from "./components/CardContainer"; class App extends Component {
state = {jokes: []} componentDidMount = () => {
fetch('http://localhost:3000/jokes')
.then(response => response.json())
.then(jokes => this.setState({jokes}))
} render() {
return (
<div className="content-container">
<CardContainer jokes={this.state.jokes} />
</div>
);
}
} export default App;
The very same result can be achieved using a functional component, with two of React’s most prominent hooks: useEffect() and useState(). The main differences from a surface-level standpoint are:
- “class App extends Component{}” becomes “function App(){}”.
- You don’t need to wrap the return statement in a render function.
- You no longer need to use “this.” syntax, such as this.state or this.myFunction.
- useState replaces state = {} and this.setState().
- useEffect replaces componentDidMount, with one huge caveat: do not forget the dependency array!
- useEffect and useState must be imported, while you can drop the Component import.
Functional Component
import './App.css';
import React, { useState, useEffect } from 'react';
import CardContainer from "./components/CardContainer"; function App(){ const [jokes, setJokes] = useState({}) useEffect(() => {
fetch('http://localhost:3000/jokes')
.then(response => response.json())
.then(jokes => setJokes(jokes))
},[]) return (
<div className="content-container">
<CardContainer jokes={jokes} setJokes={setJokes}/>
</div>
);
} export default App;
If using useEffect for a fetch call, do not forget that empty dependency array. Leaving it out will result in an infinite loop that hits the API thousands of times in mere minutes. The dependency array allows you to specify conditions which, when met, tells useEffect to execute a re-render. By providing an empty array, you’re telling useEffect to run only once upon mounting, similar to the functionality of componentDidMount. I’ve bolded it below for emphasis, as forgetting this step is one of the most common errors fledgling React programmers make. This DEV blog post by Max Rozen offers more detail about useEffect and its dependencies, if you choose to go further down the rabbithole.
useEffect(() => {
fetch('http://localhost:3000/jokes')
.then(response => response.json())
.then(jokes => setJokes(jokes))
},[])
The useState hook, in essence, increases readability and decreases potential confusion. You can pass state down using a variable name of your own choosing — in this case, jokes is assigned an array of joke objects. You can change this state directly using the associated setJokes function, which operates the same as this.setState.
I hope this simple example helps a future React student understand the differences between a class component and a functional component, as well as increases their familiarity with their two new best friends: useEffect and useState! Happy hacking.