React’s 2019 announcement of hooks was a way to abstract logic into reusable parts. Hooks were created by React to address common problems. They are primarily an alternative to classes. Hooks allow you to create functional components using state and lifecycle methods. You might have noticed function components replacing all class components since the React 16.8 update, which introduced hooks to the function component.
Hooks are not meant to replace your understanding of React concepts. Instead Hooks provide an API that is more directly related to the React concepts: props, state, context, refs, and lifecycle. Hooks offer a powerful way to combine these concepts. React Hooks are backward-compatible, meaning that they do not have any breaking changes. You can add state to a function component you have written. Previously, you had to convert it to a class. You can now do this by inserting a Hook into an existing function component.
Hook state is a new way to declare a state in React apps. It uses the useState() functional component to set and retrieve state. The following example will help you understand Hook state:
import React, { useState } from ‘react’;
function Example() {
// Declare a new state variable, which we’ll call “count”
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In the example above, useState represents the Hook that must be called inside a function component in order to add some local states to it. UseState returns a pair whose first element is the current state/initial value and the second is a function that allows us to update it.
useEffect is another Hook that can be called in a function component to add an effect to it. It is used to create side-effects. Side effects include data fetching, subscription setup, and manual DOM changes in React components.
In the following example, we have set the title of the document to a custom message and the number clicks.
import React, { useState, useEffect } from ‘react’;
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Function components are much simpler and require far less boilerplate. They are, in my opinion, a little more flexible with hooks or custom hooks, as well as being (usually) more performant. Developers are eager to incorporate the concept into their existing React applications and new applications.
This article will show you how to convert React classes into functional components with React Hooks.
But before that let us understand the difference between class and function components. Simply put, one is a class and the other is a function. The class component must extend the React Component Class and specify a render method. Whereas the function component is a function and the render method is its return value.
// Class component example
class MyComponent extends React.Component {
render() {
Return
Hello, this.props.name
}
}
//Function component example
function MyComponent(props) {
Return
Hello, props.name
}
Now, I will show you 5 ways to convert react class components into functional components using react hooks.
Approach 1: Class without state or lifecycle methods
Let’s begin with a simple React Class that doesn’t have either state or lifecycle components. Let’s create a class that alerts the user when they click a button.
import React, { Component } from ‘react’;
class App extends Component {
alertName = () => {
alert (“Jack Niccon”)
}
render() {
return (
<div>
<h3> This is a Class Component </h3>
<button onClick={this.alertName}> Alert </button>
</div>
);
}
}
export default App;
This is a standard React class. It’s nothing special or new. This class does not have any state or lifecycle methods. It simply alerts a name whenever a button clicks. This will be the functional equivalent of this class:
import React from ‘react’;
const App = () => {
const alertName = () => {
alert (“Jack Niccon”)
}
return (
<div>
<h3> This is a Functional Component </h3>
<button onClick={ alertName }> Alert </button>
</div>
);
}
export default App;
Similar to the class component, this is nothing new or different. Hooks and other new concepts have not been used yet. We have only looked at an example that doesn’t require state or lifecycle. Let’s now change this and examine situations in which we have a state-based class component and how Hooks can be used to transform it into a functional one.
Approach 2: Classes With State
Let’s say we have a global variable named “name” that we can update in the app using a text input field. React handles cases like these by setting the name variable in a state object and then calling setState() to get a new value to update it with.
import React, { Component } from ‘react’;
class App extends Component {
state = {
name: ”
}
alertName = () => {
alert(this.state.name);
};
handleNameInput = e => {
this.setState({ name: e.target.value });
};
render() {
return (
<div>
<h3>This is a Class Component</h3>
<input
type=”text”
onChange={this.handleNameInput}
value={this.state.name}
placeholder=”Your Name”
/>
<button onClick={this.alertName}>
Alert
</button>
</div>
);
}
}
export default App;
The Alert button alerts users when they enter a name into the input field. This is yet another simple React concept. However, Hooks can be used to convert the entire class into functional React components.
import React, { useState } from ‘react’;
function App() {
const [name, setName] = useState(‘Jack Niccon’);
const alertName = () => {
alert(name);
};
const handleNameInput = e => {
setName(e.target.value);
};
return (
<div>
<h3>This is a Functional Component</h3>
<input
type=”text”
onChange={handleNameInput}
value={name}
placeholder=”Your Name”
/>
<button onClick={alertName}>
Alert
</button>
</div>
);
};
export default App;
We introduced the useState hook. It allows you to make use of state in React functional parts. This functional component can now use theuseState() Hook. It has a similar syntax that allows for destructuring assignments to arrays. Take a look at this line:
const [name] = useState (“Jack Niccon”)
Name is the equivalent to this.state in a normal component class, while setName is equivalent to this.setState. It is important to remember that the useState() hook takes an argument that serves as the initial state value. The useState() argument, in simple terms, is the state’s initial value. We set it to Jack Niccon in our example so that Jack Niccon is the initial state for the name in state.
This is how to convert a React class component with state into a functional one using Hooks. As we will see, there are many other ways to do this.
Approach 3: Classes with Multiple State Properties
While it is easy to convert one state property using useState, this approach doesn’t work when dealing with multiple state properties. If we had input fields for userName firstName and lastName, then we could create a class-based component with three state property types like this:
import React, { Component } from ‘react’;
class App extends Component{
constructor(){
super();
this.state = {
userName:”,
firstName: ”,
lastName: ”
}
this.handleUserNameInput = this.handleUserNameInput.bind(this)
this.handleFirstNameInput = this.handleFirstNameInput.bind(this)
this.handleLastNameInput = this.handleLastNameInput.bind(this)
}
logName = () => {
// do whatever with the names … let’s just log them here
console.log(this.state.userName)
console.log(this.state.firstName)
console.log(this.state.lastName)
}
handleUserNameInput = e =>{ this.setState({ userName: e.target.value}) }
handleFirstNameInput = e =>{ this.setState({ firstName: e.target.value}) }
handleLastNameInput = e => { this.setState({ lastName: e.target.value}) }
render(){
return (
<div>
<h3> This is a Class Component </h3>
<input type=”text” onChange={this.handleUserNameInput} value={this.state.userName} placeholder=”Your username”/>
<input type=”text” onChange={this.handleFirstNameInput} value={this.state.firstName} placeholder=”Your firstname”/>
<input type=”text” onChange={this.handleLastNameInput} value={this.state.lastName} placeholder=”Your lastname”/>
<button className=”btn btn-large right” onClick={this.logName}> Log Names </button>
</div>
);
}
}
export default App;
We will need to go a different route to convert this class into a functional component using Hooks. The above example can be written using the useState() Hook.
import React, { useState } from ‘react’;
const App = () => {
const [userName, setUsername] = useState(“”)
const [firstName, setFirstname] = useState(“”)
const [lastName, setLastname] = useState(“”)
const logName = () => {
// do whatever with the names… let’s just log them here
console.log(userName)
console.log(firstName)
console.log(lastName)
}
const handleUserNameInput = e => { setUsername(e.target.value) }
const handleFirstNameInput = e => { setFirstname(e.target.value) }
const handleLastNameInput = e => { setLastname(e.target.value) }
return (
<div>
<h3> This is a functional Component </h3>
<input type=”text” onChange={handleUserNameInput} value={userName} placeholder=”username…”/>
<input type=”text” onChange={handleFirstNameInput} value={firstName} placeholder=”firstname…”/>
<input type=”text” onChange={handleLastNameInput} value={lastName} placeholder=”lastname…”/>
<button className=”btn btn-large right” onClick={logName}> Log Names </button>
</div>
);
}
export default App;
This shows how to convert a class-based component with multiple state properties into a functional component by using the useState() Hook.
Approach 4: Class with State and componentDidMount
Let’s take a class with componentDidMount and state. Let’s take an example, a situation where you create an initial state for each of the input fields and then have them update to a new set of values every five seconds.
This is possible by declaring an initial state value in the input fields and then implementing a componentDidMount() Lifecycle method that will run following the initial render to update state values.
import React, { Component } from ‘react’;
class App extends Component {
state = {
// initial state
userName: ‘JackNiccon’,
firstName: ‘Jack’,
lastName: ‘Niccon’
}
componentDidMount() {
setInterval(() => {
this.setState({
// update state
userName: ‘JessyNiccon’,
firstName: ‘Jessy’,
lastName: ‘Niccon’
});
}, 5000);
}
logName = () => {
console.log(this.state.userName);
console.log(this.state.firstName);
console.log(this.state.lastName);
};
handleUserNameInput = e => {
this.setState({ userName: e.target.value });
};
handleFirstNameInput = e => {
this.setState({ firstName: e.target.value });
};
handleLastNameInput = e => {
this.setState({ lastName: e.target.value });
};
render() {
return (
<div>
<h3>This is a Class Component</h3>
<input
type=”text”
onChange={this.handleUserNameInput}
value={this.state.userName}
placeholder=”Your Username”
/>
<input
type=”text”
onChange={this.handleFirstNameInput}
value={this.state.firstName}
placeholder=”Your First Name”
/>
<input
type=”text”
onChange={this.handleLastNameInput}
value={this.state.lastName}
placeholder=”Your Last Name”
/>
<button
className=”btn btn-large right”
onClick={this.logName}
>
Log Names
</button>
</div>
);
}
}
export default App;
The initial values of the state object will be used in the input fields when the app starts. After five seconds, these values will be updated to the values that you have defined in the componentDidMount() function.
Next, convert this class into a functional component with the useEffect Hooks and React useState.
import React, { useState, useEffect } from ‘react’;
function App() {
const [userName, setUsername] = useState(‘JackNiccon’);
const [firstName, setFirstname] = useState(‘Jack’);
const [lastName, setLastname] = useState(‘Niccon’);
useEffect(() => {
setInterval(() => {
setUsername(‘JessyNiccon’);
setFirstname(‘Jessy’);
setLastname(‘Niccon’);
}, 5000);
});
const logName = () => {
console.log(userName);
console.log(firstName);
console.log(lastName);
};
const handleUserNameInput = e => {
setUsername({ userName: e.target.value });
};
const handleFirstNameInput = e => {
setFirstname({ firstName: e.target.value });
};
const handleLastNameInput = e => {
setLastname({ lastName: e.target.value });
};
return (
<div>
<h3>This is a Functional Component</h3>
<input
type=”text”
onChange={handleUserNameInput}
value={userName}
placeholder=”Your Username”
/>
<input
type=”text”
onChange={handleFirstNameInput}
value={firstName}
placeholder=”Your First Name”
/>
<input
type=”text”
onChange={handleLastNameInput}
value={lastName}
placeholder=”Your Last Name”
/>
<button
className=”btn btn-large right”
onClick={logName}
>
Log Names
</button>
</div>
);
};
export default App;
This component functions exactly the same as the previous one in terms of functionality. This component uses the same functionality as the class component, except that you no longer use the state object and componentDidMount() lifecycle methods like you did with the class component. Instead, you use the useState or useEffect hooks.
Approach 5: Convert PureComponent into React memo
A component is similar to React PureComponent. The major difference between them is that React.Component doesn’t implement the shouldComponentUpdate() lifecycle method while React.PureComponent does. React.PureComponent can be used to improve performance in certain cases where render() returns the same result with the same props or state.
React.memo() operates in the same way. To improve performance, wrap your function component in a React.memo() call if it returns the same result with the same props. PureComponent and React.memo() give React apps a significant performance boost as they are able to reduce the number of render operations. You will need to first examine the code that renders each component every two seconds. This is regardless of whether there has been a change in state or value.
import React, { Component } from ‘react’;
function Unstable(props) {
// monitor how many times this component is rendered
console.log(‘Unstable component Rendered’);
return (
<div>
<p>{props.value}</p>
</div>
);
};
class App extends Component {
state = {
value: 1
};
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: 1 };
});
}, 2000);
}
render() {
return (
<div>
<Unstable value={this.state.value} />
</div>
);
}
}
export default App;
If you run the app and look at the logs, the app renders the component every two seconds. There is no change in state or props. This is a terrible situation, but we created it to show you how we fixed it with PureComponent as well as React.memo().
We only want to render a component if there has been a change of state or props. This is how we can fix this terrible situation with PureComponent. The component will only re-render if there has been a change of state or props. This is done by importing PureComponent, and then extending it as follows:
import React, { PureComponent } from ‘react’;
function Unstable(props) {
console.log(‘Unstable component Rendered’);
return (
<div>
<p>{props.value}</p>
</div>
);
};
class App extends PureComponent {
state = {
value: 1
};
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: 1 };
});
}, 2000);
}
render() {
return (
<div>
<Unstable value={this.state.value} />
</div>
);
}
}
export default App;
If you run the app again, the first render will be displayed. Because, instead of class App extends Component {}, we now have class App extends PureComponent {}. This resolves the issue of components being re-renewed without regard to their current state. But, this would create another problem if you were to implement a state change in your setState method. Consider the following changes to setState() as an example:
Value is set at 1 currently.
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: 1 };
});
}, 2000);
}
Let’s take a look at a situation in which value is assigned to Math.random()
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: Math.round(Math.random()) };
});
}, 2000);
}
This would mean that the first example component would re-render each time the value changes to the next random number. PureComponent allows you to render components only if there have been changes in state or props. You can now learn how to use React.memo() for the same result. Wrap the component using React.memo() to accomplish this.
import React, { Component } from ‘react’;
const Unstable = React.memo(function Unstable (props) {
console.log(‘Rendered Unstable component’);
return (
<div>
<p>{props.value}</p>
</div>
);
});
class App extends Component {
state = {
val: 1
};
componentDidMount() {
setInterval(() => {
this.setState(() => {
return { value: 1 };
});
}, 2000);
}
render() {
return (
<div>
<Unstable val={this.state.val} />
</div>
);
}
}
export default App;
PureComponent achieves the exact same result. This component renders only after the initial render. It does not re-render until there is a change of state or props.
Conclusion
In this tutorial, you got to learn how to convert a class-based component into a functional one using React Hooks. We also examined a case in which a React PureComponent component class was converted to React.memo(). Make sure to update your version of React to the supported version in order to use hooks in your applications.