Function components are far less verbose, and require less boilerplate. They’re a bit more flexible with hooks and custom hooks, and they are usually a bit more performant.
Note: in the examples below, I’ve shown how to import `React` and `Component`. Note that if you’re using React 17 and above, it may no longer be necessary to explicitly import react in your code, depending on your JSX transform. If you’re not sure, just explicitly import it as I’ve done here.
What’s the difference between class components and function components?
A functional component is just a plain JavaScript function that accepts props as an argument and returns a React element. A class component requires you to extend from React. Component and create a render function which returns a React element. They both do exactly the same thing.
Example Class Component
import React from 'react';interface Props {
name: string
}class Component extends React.Component<Props> {
render() {
return <p>Hello there, {this.props.name}
}
}export default Component;
Example Function Component
import React from 'react';interface Props {
name: string
}const Component: React.FC<Props> = ({ name }) => (
<p>Hello there, {name}</p>
)export default Component;
Both components take a prop (name) and render `Hello there, {name}`. It’s an extremely simple example but already we can see some of the differences.
The class component needs to extend the React Component class and must specify a render method. Whereas the function component is simply a function, and the render method is simply the return value of the function.
Not all class components can be converted to functions!
There are still some cases where you need to use a class component. But 99% of the time you’ll be fine with a function component.
There are some use cases where a function component simply won’t work. We’ll quickly discuss a couple:
If you need a constructor
If you really, really need a constructor, you’re gonna have a bad time. A constructor runs once and only exactly once, before the first render of the component.
If you need to extend a component
In Javascript, classes can extend other classes, thus inheriting the parent’s prototype. In fact, if you’re creating a class component, you have to extend the base component from React. This is more or less not possible with function components, so I wouldn’t bother trying
Higher order components
You can make a HOC (higher order component) with a function, however it can often be a bit easier to use a class.
Using hooks to replace setState
this.setState doesn’t exist any more in our function component. Instead we need to replace each of our setState calls with the relevant state variable setter.
Example Class Component setState
import React from 'react';class Component extends React.Component {
onClickHandler() {
this.setState({ count: this.state.count + 1 })
}
render (
<div>
<p>Count: {this.state.count}<p>
<button onClick={onClickHandler}></button>
</div>
)
}export default Component
Example Function Component setState with hook
import React, { useState } from 'react';const Component: React.FC = () => {
// hook useState
const [count, setCount] = useState(0)
const onClickHandler = () => {
setCount(count + 1);
} render (
<div>
<p>Count: {count}<p>
<button onClick={onClickHandler}></button>
</div>
)
}export default Component
useEffect for state update side effects
Remember how this.setState could accept a callback that would run after the state was updated? Well our useState updater function does no such thing. Instead we have to use the useEffect hook, useEffect will trigger whenever and of it’s dependencies are changed.
import React from 'react';class Component extends React.Component {
onClickHandler() {
// If you do this after your state is changed
this.setState({ count: this.state.count + 1 }, () => {
console.log('Counter was updated!')
})
} render (
<div>
<p>Count: {this.state.count}<p>
<button onClick={onClickHandler}></button>
</div>
)
}export default Component
With useEffect hook
import React, { useEffect, useState } from 'react';const Component: React.FC = () => {
const [count, setCount] = useState(0) useEffect(() => {
console.log('Counter was updated!')
}, [count])
const onClickHandler = e => {
setCount(count + 1);
} return (
<div>
<p>Count: {count}<p>
<button onClick={onClickHandler}></button>
</div>
)
}export default Component
Lifecycle methods with hooks
Instead of using the componentDidMount method, use the useEffect hook with an empty dependency array.
useEffect(()=>{
console.log('component mounted!')
},[])
Instead of using the componentWillUnmount method to do cleanup before a component is removed from the React tree, return a function from the useEffect hook with an empty dependency array;
useEffect(() => {
console.log('component mounted')
// function to execute at unmount
return () => {
console.log('component will unmount')
}
}, [])
If you pass nothing as the second argument to useEffect, it will trigger whenever a component is updated. So instead of using componentDidUpdate
useEffect(() => {
console.log('component updated!')
})
I hope that you enjoy this article! Remember, the Force will be with you always.