If you're new to React, and you've been working through tutorials, chances are you've run into examples of both functional components with Hooks, and class components, with no strong indication of which one you should be using. Even as a seasoned developer, you might still be using class components, wondering if its worth the rewrite.
Heck, you might even be thinking:
I've been digging for answers, but I just can't find a clear answer for this
That's fair enough, even the official documentation didn't have a strong recommendation until the middle of 2020.
Which one should you use?
The official React team stance (according to the docs), is:
When you’re ready, we’d encourage you to start trying Hooks in new components you write. [...] We don’t recommend rewriting your existing classes to Hooks unless you planned to rewrite them anyway (e.g. to fix bugs).
To summarise:
- New code should use functional components with Hooks, when you're ready
- Old code can keep using class components, unless you want to rewrite
Should I just focus on hooks then?
It's not that simple.
You still need class components to build Error Boundaries.
On top of that, most code written before 2019 will likely still use class components, as there is no immediate need to rewrite them to functional components with Hooks. If you want to understand existing code in a codebase, you'll need to also learn class components.
You'll also find that companies that ask React questions during their interviews will still ask you about classes.
Should we rewrite our old class-based code to use Hooks?
As with all good things, there are tradeoffs to consider here.
Hooks result in much cleaner, easier to understand components compared to class components of a similar complexity.
To illustrate my point, compare this component that fetches some data from The Star Wars API, written first as a class, then as a functional component with hooks:
import React from 'react';
export default class DataDisplayer extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
async componentDidMount() {
const response = await fetch(
`https://swapi.dev/api/people/${this.props.id}/`
);
const newData = await response.json();
this.setState({ data: newData });
}
componentWillUnmount() {
// if this was a subscription, we'd need clean-up here
console.log('CWU');
}
render() {
const { data } = this.state;
if (data) {
return <div>{data.name}</div>;
} else {
return null;
}
}
}
A pretty standard class component.
As your app grows, the lifecycle methods grow larger, and the context switching involved just from scrolling through the file increases.
I don't know about you, but my thought process when scrolling through classes is like:
Okay so I'm in
componentDidMount
, so we'll be fetching data hereOkay so here we're rendering, so this code runs every single time
I need to add some extra functionality... hmm which lifecycle method does that go in again?
On the other hand, you have Hooks:
import React, { useEffect, useState } from 'react';
export default function DataDisplayer(props) {
const [data, setData] = useState('');
useEffect(() => {
const getData = async () => {
const response = await fetch(`https://swapi.dev/api/people/${props.id}/`);
const newData = await response.json();
setData(newData);
};
getData();
return () => {
// if this was a subscription, we'd need clean-up here
};
}, [props.id]);
if (data) {
return <div>{data.name}</div>;
} else {
return null;
}
}
With Hooks, writing code that follows sequentially is much easier, and I find reading functional components with Hooks requires less context switching, as you're not jumping around the file to find which lifecycle method you think something happened in.
That's the main benefit of rewriting to Hooks - your codebase's developer experience improves as it takes less time to understand what each component does.
The main drawback is time - time spent rewriting is time you could have spent building new features.
Where to from here?
When introducing Hooks to my team in the past I recommended the following approach, and it worked quite well:
- All new code should be written as functional components with Hooks
- Existing code should only be rewritten if it gets touched (for example, if you're fixing a bug or adding functionality, take the time to swap the component over to Hooks)
(This is an article posted to my blog at maxrozen.com. You can read it online by clicking here.)
Top comments (0)