React: generator components
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. - 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.