React useMemo Destructuring

Contents

Intro

I’m assuming that you are familiar with useMemo, where we can cache both values as well as objects, functions etc from having to be re-calculated every time a component or useEffect hook re-renders. Very useful.

But what do we do in a situation where I have a set of expensive functions which are generated based on one piece of state, and then used in a useEffect in a functional component. Do I need to make a new memo for each of those functions, even though they all hinge on the same piece of state?

Turns out no, you don’t. You can put all those functions in an object, memoize the object, and then just destructure it.

For all the people saying this is too abstract, when would you ever need to do this, here is the actual situation. Skip ahead if this bores you.

We are building this sunburst chart.

sunburst chart

This chart takes a big ball of hierarchical data called a root, where every node in the chart is an object with many pieces of data. In order to color each individual node we are creating a set of color scales for each tier of the chart. These color scales are functions that take a value and output a color. We need a different scale for each tier because the inner tiers are aggregated values of the outer tiers, which would render the color scale meaningless if they all used the same scale. Each time a scale is calculated we have to analyze the big data ball (root) for the minimum and maximum values in each tier depending on the data accessor we are using for the color scale value. This is a relatively expensive calculation.

Meanwhile, this chart is responsive using a method that we developed here:

Ncoughlin: D3 React Responsive Chart

Ncoughlin: D3 Responsive Chart Scaffold

Which means that if you are resizing the window, the entire chart is getting re-rendered multiple times per second. If we are re-calculating those color scales multiple times per second that is a big ouch!

They need to be memoized. But I don’t want to make a whole set of multiple memos that re-use the same switch cases in each one, because DRY. And so we have arrived at…

useMemo Destructuring

The pattern is actually pretty self explanatory, and so useful…

import React, {useMemo, useEffect} from "react";

const TestFunctional = () => {
  const { test1, test2, test3 } = useMemo(() => {
    let test1;
    let test2;
    let test3;
    switch (someState) {
      case "case1":
        test1 = () => {
          console.log("TEST 1");
        };
        test2 = () => {
          console.log("TEST 2");
        };
        test3 = () => {
          console.log("TEST 3");
        };
        break;
      default:
        test1 = () => {
          console.log("DEFAULT");
        };
        test2 = () => {
          console.log("DEFAULT");
        };
        test3 = () => {
          console.log("DEFAULT");
        };
    }
    return { test1, test2, test3 };
  }, [someState]);

  useEffect(() => {
    test1();
    test2();
    test3();
  }, [test1, test2, test3]);

  return <></>;
};

export default TestFunctional;

Instead of needing to repeat the switch case multiple times for each function, I can do them all at once. Game changer!