React Hooks: Migrate class component to functional and use hooks
Anand Kumar
Posted on May 12, 2019
We all are aware that react hooks is ready for prime time and we started using it already. But, what if we have any existing project and we want to move to react hooks? Definitely, we have components which is written as class component.
This post will guide you to change an existing class based component to react hook based component. But, this post is not intended to go into the details of hooks.
This is what react documentation says about hooks
.
Hooks let us use state and other React features without writing a class.
Let's begin with an example application. It is a simple counter application. The code is given below:
const initialData = {
count: 0,
maxLimit: 10
};
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: this.props.initialCount
};
}
onIncrement = () => {
this.setState({ count: this.state.count + 1 });
};
onDecrement = () => {
this.setState({ count: this.state.count - 1 });
};
render() {
return (
<div>
<button
onClick={this.onDecrement}
disabled={this.state.count === this.props.initialCount}
>
-
</button>
<div>{this.state.count}</div>
<button
onClick={this.onIncrement}
disabled={this.state.count === this.props.maxLimit}
>
+
</button>
</div>
)
}
}
class CounterApp extends React.Component {
render() {
const { initialData } = this.props;
return (
<React.Fragment>
<h2>Counter Example</h2>
<Counter
initialCount={initialData.count}
maxLimit={initialData.maxLimit}
/>
</React.Fragment>
);
}
}
ReactDOM.render(
<CounterApp initialData={initialData} />,
document.getElementById('root')
);
As you can see, I have created a simple counter app. The class component that I have created are:
-
CounterApp
: This is the main app component -
Counter
: The actual counter component
Please note that, this code is intended for this post and for explanation purpose only. Please ignore any improvement areas that you may see. ☺
So, to convert a class component to functional/hooks component, here are the steps:
Pick the smallest class component first i.e. bottom to top approach would be better.
- Move all of the code that a class component has into the render method and make it work. Eg. event handler functions or any other functions or code.
- Update the code to use object de-structuring for
props
&states
. - Once, #2 is complete, Add a line before the
class
syntax like shown below.
const Counter = () => {};
class Counter extends React.Component {
/*...code goes here */
};
- Move everything from render method to the updated function component code block.
const Counter = () => {
//...code inside render methods of class component goes here
};
class Counter extends React.Component {
//...Rest of the code
render() {
// This will be blank now
}
};
- Move the de-structured code of props inside the paratenthis of functional component. Remove the code from inside the code block.
const Counter = ({ maxLimit, initialCount }) => {
/*...rest of the code */
};
- Update the component to use hooks like
useState
or any other hooks that might be needed. Eg.
const Counter = ({ maxLimit, initialCount }) => {
//...Rest of the code
const [count, setCount] = React.useState(initialCount);
//...Rest of the code
};
If you have more that one state property, please change it to use hooks for all of them individually.
I am not explaining the syntax for how to write hooks here. Please read official documentation for the same.
- Finally, update the return statement code block to use newly created props (de-structured) or the states using hooks. Or any other changes done, should be incorporated accordingly. And remove the class component line of code altogether.
Alright, that might look too much and not very clear. So let's work on the example that we have.
Counter Component
- Move the
onIncrement
andonDecrement
code block inside the render method. Define it asconst
and update the functionsthis.onIncrement
andthis.onDecrement
toonIncrement
andonDecrement
respectively. - Use object de-structuring for
props
andstate
inside render method. In our example, you can do something like below:
const { maxLimit, initialCount } = this.props;
const { count } = this.state;
- Now, just before the
class Counter extends React.Component {
line, create a line similar to following:
const Counter = () => {};
- Cut everything from
render
method and paste it in between the curly brackets of theconst Counter = () => {}
- Move the de-structured
props
into parenthesis. Line 2 is commented out and Line 1 has the props now{maxLimit, initialCount}
.
const Counter = ({ maxLimit, initialCount }) => {
// const { maxLimit, initialCount } = this.props;
const { count } = this.state;
// ....Rest of the code
}
- Next, we need to change our
this.state
. We will be usinguseState
hooks here. Update the code at Line 2 as shown below:
const Counter = ({ maxLimit, initialCount }) => {
const [count, setCount] = React.useState(initialCount);
// ....Rest of the code
}
The code that we changed is syntax of using hooks.
- Now is the time to update our function to use
setCount
we defined using hooks. Change the codethis.setState
that we have insideonIncrement
andonDecrement
. Updated code will look something like below:
const Counter = ({ maxLimit, initialCount }) => {
// ....Rest of the code
const onIncrement = () => {
setCount(count + 1);
};
const onIncrement = () => {
setCount(count - 1);
};
// ....Rest of the code
}
- Now, update the code of return statement accordingly to use the new
count
state (created using hooks) and de-structuredprops
. AND, remove theclass Counter extends React.Component {}
code completely.
The final code for Counter
component should look like:
const Counter = ({ maxLimit, initialCount }) => {
const [count, setCount] = React.useState(initialCount);
const onIncrement = () => {
setCount(count + 1);
};
const onDecrement = () => {
setCount(count - 1);
};
return (
<div>
<button
onClick={onDecrement}
disabled={count === initialCount}
>
-
</button>
<div>{count}</div>
<button
onClick={onIncrement}
disabled={count === maxLimit}
>
+
</button>
</div>
)
}
So, you must notice that at the end, there is no this
being used and code also looks lot more cleaner.
CounterApp
The main CounterApp
component doesn't have much in it and so we can convert it to functional component without hassle. The final code for CounterApp
will look like below.
const CounterApp = ({ initialData: {count, maxLimit} }) => {
return (
<>
<h2>Counter Example</h2>
<Counter initialCount={count} maxLimit={maxLimit} />
</>
)
}
Wondering what
<>
and</>
is? This is an approach to avoid extra layer of elements that we used to created earlier. So, use this or useReact.Fragment
as shown at the beginning of the article.
As, we have refactored our code now, the complete counter app code is given below:
const initialData = {
count: 0,
maxLimit: 10
};
// added style to make it look better, in case you want to see
const counterStyles = {
h1: {
maxWidth: "185px",
boxShadow: "5px 5px 4px #888888"
},
container: {
display: "flex",
alignItems: "center"
},
button: {
width: "30px",
borderRadius: "50%",
borderStyle: "none",
fontSize: "22px",
backgroundColor: "khaki",
height: "30px",
paddingBottom: "5px",
cursor: "pointer"
},
countDiv: {
minWidth: "80px",
padding: "0 15px",
textAlign: "center",
height: "30px",
lineHeight: 2,
border: "1px solid",
borderRadius: "10px",
margin: "0 5px"
}
};
const Counter = ({ maxLimit, initialCount }) => {
// styles...
const { container, button, countDiv } = counterStyles;
const [count, setCount] = React.useState(initialCount);
const onIncrement = () => {
setCount(count + 1);
};
const onDecrement = () => {
setCount(count - 1);
};
return (
<div style={container}>
<button
style={button}
onClick={onDecrement}
disabled={count === initialCount}
>
-
</button>
<div style={countDiv}>{count}</div>
<button
style={button}
onClick={onIncrement}
disabled={count === maxLimit}
>
+
</button>
</div>
);
};
const CounterApp = ({ initialData: {count, maxLimit} }) => {
return (
<>
<h2 style={counterStyles.h1}>Counter App: Hooks</h2>
<Counter initialCount={count} maxLimit={maxLimit} />
</>
);
};
ReactDOM.render(
<CounterApp initialData={initialData} />,
document.getElementById('root')
)
Summary
Through this post, we have learnt couple of tips on how to convert a class based component to functional and hook based component. I hope this will help you to migrate your existing code base.
I have also recorded a video to accompany this article. Here is the link.
Feel free to subscribe to the channel and explore other videos too.
In case of any questions/concerns, feel free to reach out.
Happy Learning!
Posted on May 12, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 28, 2024