When coding, usually we start out with the simplest, most verbose way of writing code. While it's better to identify common logic early on and write reusable abstractions for it, sometimes we just have to start simple and refactor later. In this article, I use a small refactoring in one of Teko ERP modules to introduce us to three React patterns.

Hi, I'm Thoa, a developer in the Finance Management team based in HCMC. I joined Teko Vietnam late February 2019 and have been working on the web front-end for the ERP Finance module since then.

Introduction

  • What is FM webapp? The web front-end to display and interact with data from the Finance Management micro-service (FM). Built on React.
  • What are HOCs? A higher-order component (HOC) is a function that takes a component and returns a new component, usually with added props. HOCs usually have the signature withX, for example, withStyles (material-ui v1), withRouter (react-router).
  • What are render props? A render prop is a function prop that a component uses to know what to render. It’s similar to a children prop (or the slot pattern), except that children normally contains DOM nodes, while render tends to mean a function.
  • What are hooks? Hooks are a new addition in React 16.8 (February 2019). They let you use state and other React features without writing a class. By convention, they have this signature useX, for example, useState, useEffect, useContext, useRef.

Our case study

  • The need: FM logic defines enum sets such as object types, account types, sellers, voucher types. The webapp doesn’t store these values. Instead, it fetches from the API /enums/something whenever it needs to display a drop-down, or map a code to a label.
Values for these drop-downs come from APIs like /enums/object-type
  • The problem: Code repetition (currently: 11 files). See the screenshot below.
16 results in 11 files
Code repetition when calling the API for each component individually
  • The requirement: Extract this fetching logic to a reusable function or component.

Comparing the three approaches

Heads-up: Don’t try to read the code. Just quickly scan for readability.

The base

At a glance: three approaches for building the base component

There's not much difference among these three functional components. They all do the same things:

  • hold three local state variables: loading, choices, error
  • on mount: call API /enums/something
  • pass the loading, choices, and error values to the consumer.

The consumer

Note: in the screenshots below, the rendering code was intentionally left out to focus on the shared logic.

Challenge level 1: The consumer needs one enum set.

At a glance: the three corresponding consumers

Any option looks fine.

Challenge level 2: The consumer needs multiple enum sets.

At a glance: the three corresponding consumers, when needing multiple enum sets

Look closer at each approach and what do we see?

HOC:

Consumer of the HOC approach
  • Unclear which props come from which HOCs 🤔
  • Props naming collisions 🚗

Render props:

Consumer of the render props approach
  • Nesting hell, difficult to read and comprehend 😵
  • Too many inline functions, which could affect performance 🐌

Custom hooks:

Consumer of the custom hook
  • It’s clear which attributes come from which hooks 🧐
  • Can use multiple hooks without fear of naming collisions 🦺

Conclusion

HOC, render props, and hooks are all useful React patterns that will help refactor a code base, making logic in your apps more reusable. HOCs are still in use, although several developers in the React community advise against it (Michael Jackson from ReactTraining is one of such vocal advocates). Render props are one step better. Particularly with its inline-style, this pattern makes composing simple JSX tags short and sweet, improving readability. However, as we’ve seen in the example above, this pattern can become an anti-pattern if it results in too deep nesting. Since React 16.8 and React Native 0.59, hooks entered the scene and as shown above, they can mitigate certain drawbacks of the other two patterns, such as wrapper hell and naming collisions.

  • Do I need to rewrite all my HOCs and render props components using hooks now? No, I don’t think so. The patterns should be at your mercy, not you being a slave to them. Therefore, if the existing patterns already solve your needs, let them be. Learning about patterns is just for you to have more options next time.

References