React: generator components
By: Vilius Poska
Tags: Engineering, JavaScript, Frontend, Generator, React

If you’ve ever built a form of variable fields that can dynamically change or a table with variable columns, you’ve probably dealt with a barrage of if or inline ternary statements.

I’ve found that this component building pattern below works best when:

Developing complex tables (with varying bits).

Transforming nested arrays/objects to React nodes.

The situation requires the use of procedural constructs (that you can’t inline in JSX).

Real-world example

Let’s make better sense of the problem by looking at an example. Suppose we have this data source:

The goal is to render it in React as such:

Pizza

Pepperoni

Diavola

Sausage

Bratwurst

Kielbasa

Pizza

Margherita

Simply put: iterate through the list rendering the products. If the group differs from the last one, render it before the product. The real-world scenario would likely provide additional complexities such as more category dimensions or affecting the rendered component with conditional props.

Let’s make it happen!

The inherent complexity in this problem is that created nodes depend on previous ones. The natural programming construct to encode this behavior is the generator function.

Some prerequisites

If you’ve never used generator functions in JavaScript/TypeScript, doing that is fairly straight-forward:

The key thing not to miss here is the asterisk * that goes after the function keyword.

With React >= 16 a functional component may return an array, given that the nodes have unique key s, i.e.

Tying everything together

First, we’ll implement the generator function that traverses our data structure:

Here we iterate through the menu and:

Render a h2 group label if the last label was different or it’s the first product.

group label if the last label was different or it’s the first product. Render a p product name.

This function, however, is not a component in itself, since it doesn’t return an array. Here’s the simplest component definition:

Improving efficiency

Unfortunately, this solution is not ideal in cases where the component is forced to rerendered because of internal state/prop changes. Every time this happens, your generator function will be executed. If you’re traversing complex structures based on defined inputs, you can very well leverage React’s memoization tools:

In the case above, useMemo will keep a cached version of your nodes that’s recomputed only when the menu changes. Meaning you can introduce local state or any other effects that re-renders the component without unnecessarily executing the generator.

Addendum: using multiple generators

There are cases where you’ll additionally want to access items from another generator within a generator. This becomes necessary when an additional level of nesting is introduced. The solution below is not a recommended one, since it’s always better to split components to abide by the single responsibility principle. It just goes to show the power of generators.

Conclusion

Generators are powerful and efficient construct usually overlooked in the JavaScript world. This goes to show how inherently hard React problems can be solved by using them.