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>