How I converted my React class component to functional component using Hooks
Many React developers are talking about hooks these days and if you have used React recently you would have definitely heard of it. I started learning about hooks a couple of weeks ago by going over the official ReactJS documentation. In this blog, I will share my learning on this topic. I will cover how I converted a class component to a functional component using React hooks.
What are React Hooks?
Hooks got introduced in ReactJS 16.8. Hooks enable us to use state and other react features without writing a class. At this point, you may ask
But why I should be using Hooks instead of classes
The answer is it makes code reusable. Let us look at code snippet shown below.
.......componentDidMount() {
const { searchText } = this.props;
document.title = `${searchText} - Search Results`;
this.props.searchPlanet(searchText);
}componentDidUpdate() {
const { searchText } = this.props;
document.title = `${searchText} - Search Results`;
this.props.searchPlanet(searchText);
}......
In the code snippet shown above, we updated the title when the component is mounted or updated using React lifecycle methods. We could make the above code better by extracting a helper method and calling it from both the lifecycle methods. But, in any case, we will have to ensure that we call the method from both the lifecycle methods.
The problem is more prominent when we use the same functionality in multiple components. Traditionally, in React if we want to reuse code we go with Higher-order component or Render Props. In my experience, using them leads to complex code.
Hooks are introduced to solve such problems. We can use hooks to organize our code into independent units that can be reused not across components only but within components as well!.
Show me the code
Let’s look at a code example to understand this in detail. In the code shown below, we are using useState
and useEffect
hooks ( part of the React API ) to create a custom hook. The hook encapsulates the logic to get coordinates of a given element and keep it updated if window resizes.
Now, this hook can be used anywhere with just one-liner. Isn’t it cool!
Transformation
Below is an example of a class component . As you can see we are using state and lifecycle methods.
Me (before learning hooks): If we need to convert this component to functional one then we need to remove the use of state and lifecycle methods
React (≥16.8): Hold my beer!
First, make sure you have React version supporting hooks, if you are using an older version then update the version in your package.json.
"react": "^16.8.0",
"react-dom": "^16.8.0",
then run npm install or yarn install
The first thing I changed to make my class component to functional component is to remove the usage of this.state
and this.setState
.
// Original Componentimport React, { Component } from "react";class Login extends Component {
state = { showAlert: false };
....
}
// Updated Componentimport React, { useState } from "react";function Login(props) {
const [showAlert, setShowAlert] = useState(false);
....
}
As you can see to remove state
we imported useState
hook.
useState
is a Hook that lets you add React state to function components.
Let's take a closer look at this line
const [showAlert, setShowAlert] = useState(false);
.
As you can see, we called useState
hook with false
. The argument with which useState hook is called will become initial state value. The method returns an array with a pair of values. The first is a state variable and the second one is the setter function. It is similar to this.state.showAlert
and this.setState
in a class. If we wanted to store two different values in state, we would call useState()
twice.
Next change I made to class component was to replace lifecycle methods with hooks
// Original component
import React, { Component } from "react";
....componentWillReceiveProps(newProps) {
let { loginInfo } = newProps;
if (loginInfo.success) {
this.props.history.push('/search');
} else if (!loginInfo.success && loginInfo.showAlert) {
this.setState({
showAlert: true,
alertText: loginInfo.alertText
});
}
}// Updated component
import React, { useState, useEffect } from "react";function Login(props) {
const [showAlert, setShowAlert] = useState(false);
const [alertText, setAlertText] = useState("");
.... useEffect(() => {
function updateComponent() {
let { loginInfo } = props;
if (loginInfo.success) {
props.history.push("/search");
return;
}
if (!loginInfo.success && loginInfo.showAlert) {
setShowAlert(true);
setAlertText(loginInfo.alertText);
}
}
updateComponent();
});....
Here you will notice that I imported a new hook useEffect
and declared one more state variable alertText
.
What is Effect hook?
The Effect Hook lets you perform side effects in function components
fetching data, adding/removing listeners to events or changing DOM manually. These are all examples of side effects.
We all had faced a situation where we need to perform some action after the DOM has been updated. For example network requests and logging, which are side effects. Effect hook let us perform these side effects after every render.
So, if you compare it with class components lifecycle methods it is a combination of componentDidMount
, componentDidUpdate
and componentWillUnmount
You might be thinking that I replaced componentWillRecievProps
lifecycle method with useEffect
hook, isn’t it wrong?
No. This is one of the mistakes I made when I started learning hooks. I tried to map hooks concepts with class component features, which is wrong. You should look for problem patterns and solve them with hooks. This needs a shift in mindset when working with hooks. Like in my case, I want to show message box based on the response from network request. The response is updating props that will trigger a render so useEffect
hook will fit my need. After the implementation of useEffect
hook the way I have shown above I encountered one issue. The issue was that it was showing the error message alert again and again after closing. I quickly figured out the reason. The reason was that we were updating state when we are closing the alert and it triggered re-rendering of the component. This meant useEffect
hook got called again showing the error alert on the screen.
So at this point, I was looking how to run useEffect
hook only when my server response prop (loginInfo in my case) is changed. The solution was simple. We need to pass the second argument to useEffect
. It will be an array with dependency as shown in code
useEffect(() => {
function updateComponent() {
let { loginInfo } = props;
if (loginInfo.success) {
props.history.push("/search");
return;
}
if (!loginInfo.success && loginInfo.showAlert) {
setShowAlert(true);
setAlertText(loginInfo.alertText);
}
}
updateComponent();
}, [props.loginInfo]);
This means useEffect
hook will be called when props.loginInfo
gets updated.
Tip: if you pass the second argument as a blank array
useEffect
hook, the callback function will be called only once.
You can view the complete source of the functional component below.
That’s all for this post. Hope you enjoyed it. Happy coding!