React refactoring: from class to function components and Hooks

Leandro Ercoli
3 min readOct 3, 2019

--

Up until React version 16.8, we could either define a new component as a JavaScript function, that received props (or inputs) and returned a React element describing what should be displayed on the screen, or as an ES6 class with a local state and lifecycle methods.

Structuring our logic with React classes demands that we design components in terms of the lifecycle functions involved in each phase, from mounting to updating and, finally, unmounting. For instance, render(), the only required method in a React class component, is triggered every time our state changes (i.e. reacts), generating the content to be displayed. Or componentDidMount(), a heavily used lifecycle method that gets called as soon as our component is mounted (added to the DOM), and is perfect for instantiating API calls and loading data from remote endpoints.

Implementing a login form with a class

Inside a new React project we could load our main React parent element from the index.js file:

The App component would, among other things, render our login form:

The Login component would be an ES6 class that overwrites the render method to display a common HTML form containing two inputs (the username and the password) and a submit button. To keep it simple, each element would be styled through the className property that specifies a CSS class.

After importing the corresponding styling file, the login form would look something like this:

Login form UI

To manage the logic behind our form, we could save our input values in the component’s state, and call an appropriate method when the onChange event gets triggered, updating our stateful values:

Finally, when the form gets submitted, we would have to call a handleSubmit method that performs all the necessary logic to log the user (e.g. an API call to check if the user exists). The complete class component would then be:

Refactoring into a stateless function

To restructure a class component into a function component we should consider that a plain JavaScript function component is stateless, intended to keep the focus on the presentation and improve reusability through a modular approach. This means that we have to maintain our state in a parent component and pass it down to our function components as props.

To illustrate this in our form component lets encapsulate the presentation elements, returned by the render method, inside a separate function component that receives the username and password as props. Ok, but how do we change the parent’s state? Props!

ES6 tip: use destructuring on props to clean the code and improve readability!

Hooks

React 16.8 introduced Hooks to allow stateless function components to maintain its own state without the added complexity of lifecycle methods in classes, keeping the structure of our code easy to read and the stateful logic reusable. One of the basic built-in Hooks provided by React is useState:

const [state, setState] = useState(initialState);

We can call useState inside a function component to declare a new stateful value and a function to update it, both available to us by destructuring the array returned by the Hook. The argument passed to it sets the initial value of our new state element. That’s it!

Since we only needed our parent Login class component to maintain the state of our form, we can now completely replace it by adding the useState Hook to our function component.

Next steps

useState is just the tip of the Hook iceberg. We can also replace some lifecycle methods with useEffect, subscribe to contexts with useContext, and even implement our own Hooks, reusing stateful logic between components and keeping our code clean and readable without the use of classes.

--

--

Leandro Ercoli

Computer Engineer — Full-stack web developer and multi-platform mobile developer