Recursive lists in React using Context

Recursive lists in React using Context

Replicating async file structures and tree-like hierarchies in a more simple and reusable way

Recently I have been working a lot with recursive trees and displaying them in React. While working with them I came up with a pattern that I like, and that I want to share with those that run into this pattern. They can overcome this hurdle in a way that can be reused quite easily.

To start, you want to create a context that has all of the functionality each node in your recursive tree might need. Lets for the sake of this article assume we are creating a nested keyword structure that we can use to tag stuff (pictures, tweets, posts, etc…) with. Nested meaning that keywords that have children that are selected, makes themselves selected (dog -> retriever, selecting retriever, also tags it with dog). Great. The context now has functions for selecting, and deselecting keywords.

Now, you create a keyword component that consumes the context and displays something, maybe what I would call a row. The only requirement I would make here is that you should have the component accept a callback that returns a function to retrieve data (in this case, the next page of data). You pass this component as a render method to the precursor.

export const Recurser = ({ render, getItems, id }) => {
  const Component = render // for react capital convention
  const { items } = getItems(id)
  return <Component>
    {items.map(itemId => <Recurser render={render} getItems={getItems} id={itemId}/>)}   
  </Component>
}

In general your structure would look something like this:

<ContextProvider>
  <Recurser>
    <ContextConsumer&Render />
  </Recurser>
</ContextProvider>

This assumes some things.

You have to pass a function (or a react hook) to the Recurser component so that it can get children given the parent's id. A function can work, but a dynamic hook can let you connect to redux, or consume a react context.

The context provider holds the state for knowing what an item is tagged with, and provides functions for (in the case of keywords) changing that state.

The context consumer and render component connects to the context provider and probably another context or redux state. This would allow the render component to get (again for keywords) the keyword's name given the id. In the case of more complex objects, you could retrieve all properties of that object given an id.

This allows you to have different recursive lists, but keep the way they are nested and loaded. This could be good for a component in a keyword setup page, to set up possible keyword structures, and a second keyword component for tagging items with keywords.

The rendered components can have totally separate functionality and layout but keep the recursion.

Check out a codepen example here: codesandbox.io/s/compassionate-tharp-dy79p

Note: The codepen is a pretty big example, but I tried to replicate the fact that in redux you get data with selectors, and you don't get the promise (or observable, or callback or whatever) in the component itself. It uses a custom hook in the index file that has comment explanations.

Previously posted on Medium (I am the original author).

Photo by Markus Spiske temporausch.com