Since the React 16.8 update which added hooks to function components, you might have seen function components replacing class components everywhere.

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.

tarmac

Tarmac Blog

Following

 
 
 
 
  •  

 

Written by: Dave Worsell / Vice President, Sales

Let's team up!

Tarmac acquires SaaS company Usetrace to increase software quality through use of enhanced automation technology.
Find out more details Close Button