Bad Coding Practices Series: Always Memoizing

Jeremy Tong
3 min readAug 3, 2023

It’s pretty astounding to me that so many ReactJS developers don’t quite understand the usage of useMemo(), often times using it in places where it truly does not belong.

Everyone knows that useMemo() might speed up performance by memoizing data between data cycles, but few know that these optimizations are only worth it in very specific circumstances. This article goes into general detail on how it should be used.

In recent projects, however, I’ve noticed that memoization reveals an even more sinister anti-pattern beyond being used incorrectly for performance. Under the same incorrect philosophy as an optimization “catch-all”, useMemo() is used to optimistically preprocess data using a callback.

Specifically, I’ve seen this in managing data flows where:

  1. There is the transformation callback required of some raw data
  2. The transformation’s calculations are expensive (could take >1 seconds to run)
  3. The transformation may result in error states
  4. The data is not required immediately, it is usually served to a callback later

If this calculation is done in a memoized data-structure, say, it will run optimistically even if not on every render.

//...React Functional Component
const myRawData = { input1: 3, input2: 4 }

const transformMyDataExpensive: NewData = useMemo(() => {
//complexTransform, a callback of itself, may throw an error
return complexTransform(myRawData)
}, [myRawData, complexTransform])

const callbackLater = (transformedData: NewData) => {
submit(transformedData)
}

After writing the code above, the misinformed developer will run into error states that he must handle, adding additional side effects to the hook.

const [error, setError] = useState('')
const transformMyDataExpensive: NewData = useMemo(() => {
try {
//complexTransform, a callback of itself, may throw an error
return complexTransform(myRawData)
catch (error) {
setError(error.message)
}
}, [myRawData, complexTransform])

The proper place to handle async validation here would be on the myRawData input (if desired), you should know if the inputs are invalid before the transformation occurs. Sync validation upon submission will reveal issues with the transformation.

In general, it’s pretty bad to add sideEffects to a useMemo, as you’ll have to omit it from the dependencies to prevent infinite render loops.

I would attribute this mistake to the developer not fully understanding the implications of callbacks. Like in grocery shopping, groceries should not bagged and charged to your credit card until you submit checkout. In code, you should not call your callbacks within a useMemo() and optimistically pre-process your expensive data. You should call it when you need it, when submitting that data.

//...React Functional Component
const myRawData = { input1: 3, input2: 4 }

const callbackLater = (transformedData: NewData) => {
submit(complexTransform(transformedData))
}

In the rare case you need to display some text related a subset of the transformed data, I’d recommend writing a new function that extracts just the barebones information to render (e.g. getting the keys of myRawData).

//...React Functional Component
const myRawData = { input1: 3, input2: 4 }

const toDisplay = useMemo(() => {
return Object.keys(myRawData)
}, [myRawData])

const callbackLater = (transformedData: NewData) => {
submit(complexTransform(transformedData))
}

Though there is some redundancy if complexTransform also calls Object.keys(myRawData), you avoid introducing additional error states that might need to be handled upon each memoization cycle.

To recap, the main ideas here are

  1. Complex and expensive data transformations are written as callbacks, and meant to be used as callbacks
  2. Memoization does improve performance, but may force the developer to introduce unwanted side-effects in handling certain types of callbacks
  3. Hooks are meant to be headless, they should be able to exist independently of a React display component

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Jeremy Tong
Jeremy Tong

Written by Jeremy Tong

Startup-Focused Software Developer

No responses yet

Write a response