How to Prevent Nesting of Components in React

Photo by David Clode on Unsplash

How to Prevent Nesting of Components in React

When making an application/library in React it may be necessary to prevent the nesting of components. Let's use an example to help understand the problem and the solution. When creating a presentation library you might have some components like:

  • Deck -> Represents a deck of slides
  • Slide -> Represents a slide in a deck
  • Text -> Text in a slide

The consumer of the library might have a setup like:

<Deck>
  <Slide>
    <Text>What is React?</Text>
  </Slide>
  <Slide>
    <Text>Using React in Production</Text>
  </Slide>
</Deck>

It doesn't make a lot of sense to have nested slides so a setup like this should be invalid:

<Deck>
  <Slide>
    <Text>What is React?</Text>
    <Slide>
      <Text>What are hooks?</Text>
    </Slide>
  </Slide>
</Deck>

This is an invalid use of the Slide component but how do you enforce this? The answer: React Context You need to create a context for the Slide component with null as the default value:

const SlideContext = React.createContext(null);

Then, in the Slide component get the current value of SlideContext and raise an error if the context has a value:

if (useContext(SlideContext)) {
  throw Error('Slide components cannot be nested');
}

Provide a value for the context somewhere after the check above in the Slide component:

<SlideContext.Provider value={{useAnimations: true}}>
  ...
</SlideContext.Provider>

SlideContext will only have a value if it had been used previously in a parent slide since we provide a value after the check useContext(SlideContext). A nested Slide will throw an error with the message Slide components cannot be nested.

<Deck>
  <Slide>
    <Text>What is React?</Text>
    <Slide>
      <Text>What are hooks?</Text>
    </Slide>
  </Slide>
</Deck>

In the first Slide useContext(SlideContext) will return null since at this point SlideContext does not have a value provided. The value will be provided in the line:

<SlideContext.Provider value={{useAnimations: true}}>

In the nested Slide, the expression useContext(SlideContext) will return the value {useAnimations: true} which had been set in the parent Slide so the error in the next line will be thrown.

Full code:

const SlideContext = React.createContext(null);

if (useContext(SlideContext)) {
  throw Error('Slide components cannot be nested');
}

<SlideContext.Provider value={{useAnimations: true}}>
  ...
</SlideContext.Provider>