The Future of Functional React Components with React Hooks

Progyan πŸ‘¨πŸ»β€πŸ’» | #TheProDev
Geek Culture
Published in
5 min readApr 24, 2021

During React Conf 18, Sophie Alpert introduces React Hooks followed by a detailed analysis of the problem and solution by Ryan Florence.

React Hook is an experimental functionality proposed by RFC that helps developers to build easy, scalable, and maintainable Functional React Components with States and Context just like Stateful components.

Class vs Pure Function

There are few libraries out there solving these problems using Higher-Order-Components like β€˜recompose’.

From the beginning React adopted a Class-based declarative syntax for UI rendering with State, Context, and Props. State essentially became the prime feature for Reactive Programming with React framework that had a dependency on Class declaration.

With time, a group of developers encouraged another way to develop React components, and that is through pure stateless functions. A function, similar to the previous, takes a set of props as a parameter and returns JSX element that is injected to the DOM. The only problem is that functional components developed until now had no way to have their owns states. And hence required to put inside a Stateful React Class Component that has its own state and passed down to the Functional component as props.

Turn-around Solutions

Now let’s look at the approaches that help us to overcome the problem with React being as it is:

  1. Creating Only Class-based React Component: As mentioned here and here, this will include a hell of amount of unnecessary redundancy to the production build to convert all the classes to function. Also, not all components are stateful, where the use of class serves no purpose.
  2. Separation of UI and Logic: The popular way is to distinguish between the needs of components. The UI rendering is done by the functional components and where related business logic and therefore state handling is done in Container class. But the problem here is that, with increase in complexity, the fine line between stateless component and stateful container blurs. Also there is lot of switching (from stateless to stateful and vice-versa) involved and overall component count increases rapidly.
  3. Using Higher-order Components: Using some third-party library or similar to create a layer of abstraction that handles the state behaviour whereas the code-base remains fully functional. The main problem is that, it creates a long chain of hierarchy and can easily result in unmaintainable highly coupled components. And it can also create drastic drop in performance by creating larger bundle.

Introducing React Hooks

To reuse logic between stateful components whereas to keep bundle size minimum as well as to avoid HOC hierarchy hell, React community introducing a new feature on top of existing framework that is backward compatible and hence does not break existing codes. React hook consists of a set of brand new APIs that can be called upon from functional components to achieve state and context just as class.

According to the React Docs Overview of Hooks, these can be classified into following category-

  1. State Hooks- State hook is exposed by useState() API and is used to create local state for a functional component. Here, the state can be anything, from object to any primitive data type in JS. The function useState takes a single argument, the default state value and returns an array of two objects, the state and the update function, respectively. This is kind of equivalent to setState in Component class except for the fact is that the update function here acts locally to each state. For multiple states within a single component, useState must be called multiple times, if so required.
  2. Effect Hook- Effect hook is exposed by useEffect() API and is used to maintain side effects of component changes or DOM updates. Simply put, it provides an layer of abstraction to handle life-cycle of a React component from pure functional declaration. The main difference is that, each one of life-cycle event/methods in class (i.e., componentDidMount, componentDidUpdate and componentWillUnmount) are unified together in a single API. Also useEffect can be called multiple times within a component definition allowing separating each of logic event from one another.

Experimenting with Hooks

Note: Codes shown here are meant for understanding and not for reproduction.

Let us consider a simple component that renders a list of items and their prices in a table. The first approach is to declare a class and provide details in render method.

import React, { Component } from 'react';

export default class PriceList extends Component {

constructor(props) {
super(props);
this.state = {
list: []
};
}

render() {
const { list } = this.state;

return (
<table>
<thead>
<tr>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{
list.map(item =>
<tr key={item.id}>
<td>
{ item.name }
</td>
<td>
{ item.price }
</td>
</tr>
)
}
</tbody>
</table>
);
}
}

Now to get the list we need some fetch or similar API call to our data service.

import React, { Component } from 'react';

export default class PriceList extends Component {

...

componentDidMount() {
this.fetchData(); // Defined Somewhere in the Component
}

componentDidUpdate() {
this.setState({ list: this.props.data }); // Data is the incoming props
}

...
}

Now let us start converting above into functional component, first let us make the UI component for View layer.

import React from 'react';

export default ({ list }) => (
<table>
<thead>
<tr>
<th>Item</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{
list.map(item =>
<tr key={item.id}>
<td>
{ item.name }
</td>
<td>
{ item.price }
</td>
</tr>
)
}
</tbody>
</table>
);

Now before hook, we would need to create a class similar to before with state and life-cycle method and inject the list as props to the functional component we just created.

import React, { Component } from 'react';
import PriceList from './PriceList';

export default class PriceListContainer extends Component {

constructor(props) {
super(props);
this.state = {
list: []
};
}


componentDidMount() {
this.fetchData(); // Defined Somewhere in the Component
}

componentDidUpdate() {
this.setState({ list: this.props.data }); // Data is the incoming props
}

render() {
return <PriceList list={this.list} />;
}
}

Now that is not a nice code at all. All the code reduction we did in the UI component had not much effect on the logic component. Using hook we can perform similar state update and life-cycle handling within a functional component.

import React, { useState, useEffect } from 'react';
import PriceList from './PriceList';

export default () => {
const [ list, setList ] = useState([]);

useEffect(() => {
const fetchData = () => {
...
};
const data = fetchData();
setList(data);
});

return <PriceList list={list} />;
};

So we can clearly see how easily we can integrate hooks into our codebase to make it more clear and concise.

Conclusion

Hooks have enabled us to think beyond classes to create and connect React components in a web application. And it also arises a new horizon in application design and architecture and related new problems.

Originally published at https://www.linkedin.com.

--

--

Progyan πŸ‘¨πŸ»β€πŸ’» | #TheProDev
Geek Culture

Software Engineer turned Architect | Upcoming Data Engineer | Start-up Advisor | Open Source | Philanthropist