The React Context hell

What is the React Context hell?

Like the callback hell, usual when jQuery was used for everything, the React Context hell is the nasty code you get taking advantage of the React Context API.

const App = () => {
// … some code
ret…


This content originally appeared on DEV Community and was authored by Alfredo Salzillo ?

What is the React Context hell?

Like the callback hell, usual when jQuery was used for everything, the React Context hell is the nasty code you get taking advantage of the React Context API.

const App = () => {
  // ... some code
  return (
    <>
     <ReduxProvider value={store}>
      <ThemeProvider value={theme}>
       <OtherProvider value={otherValue}>
        <OtherOtherProvider value={otherOtherValue}>
         {/** ... other providers*/}
                                <HellProvider value={hell}>
                                  <HelloWorld />
                                </HellProvider>
         {/** ... other providers*/}
        </OtherOtherProvider>
       </OtherProvider>
      </ThemeProvider>
     </ReduxProvider>
    </>
  )
}

How to fix it?

To clean up the nasty code you get from taking advantage of React Context API we need a way to nest multiple Context.Provider without passing them as children of each other.

To achieve that we can use the React.cloneElement API.

The cloneElement API

React.cloneElement(
  element,
  [props],
  [...children]
)

Clone and return a new React element using element as the starting point. The resulting element will have the original element’s props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.

We can use the cloneElement API to reduce a collection of providers, this way we don't have to nest them inside each other.

return [
  <ReduxProvider value={store} />,
  <ThemeProvider value={theme} />,
  <OtherProvider value={otherValue} />,
  <OtherOtherProvider value={otherOtherValue} />,
  // ...others,
  <HellProvider value={hell} />,
  <HelloWorld />,
].reduceRight((prev, provider) => React.cloneElement(provider, {}, prev))

The last element of the array is the content of the app.

Using reduceRight we preserve the nesting to make the HelloWorld element a child of all the providers.

To make it simpler to use we can implement a MultiProvider component.

import React from 'react'

const nest = (
  children: React.ReactNode,
  component: React.ReactElement
) => React.cloneElement(component, {}, children)

export type MultiProviderProps = React.PropsWithChildren<{
  providers: React.ReactElement[]
}>

const MultiProvider: React.FC<MultiProviderProps> = ({
  children,
  providers
}) => (
  <React.Fragment>
    {providers.reduceRight(nest, children)}
  </React.Fragment>
)

export default MultiProvider

Now we can refactor the example using the MultiProvider.

const App = () => {
  return (
    <MultiProvider
      providers={[
        <ReduxProvider value={store} />,
        <ThemeProvider value={theme} />,
        <OtherProvider value={otherValue} />,
        <OtherOtherProvider value={otherOtherValue} />,
        // ...others,
        <HellProvider value={hell} />,
      ]}
    >
      <HelloWorld />
    </MultiProvider>
  )
}

You can find an implementation of MultiProvider inside the react-pendulum library.

GitHub logo alfredosalzillo / react-pendulum

A React context utility library

react-pendulum

Use the power of the Pendulum and change the course of the duel!

react-pendulum a React Context utility libraries

NPM JavaScript Style Guide codecov

Install

Using npm

npm install --save react-pendulum

Using yarn

yarn add react-pendulum

Components

MultiProvider

A component to nicely and readably wrap components with multiple providers

Props

  • providers the array of providers instances to wrap to the children
import React, { Component, createContext } from 'react'
import { MultiProvider } from 'pendulum'
const FirstNameContext = createContext<string>('John')
const LastNameContext = createContext<string>('Doe')
const HelloWorld = () => {
  const firstName = useContext(FirstNameContext)
  const lastName = useContext(LastNameContext)
  return <>{`Hello ${firstName} ${lastName}`}</>
}
class App extends Component {
  render() {
    return (
      <MultiProvider
        providers={[
          <


This content originally appeared on DEV Community and was authored by Alfredo Salzillo ?


Print Share Comment Cite Upload Translate Updates
APA

Alfredo Salzillo ? | Sciencx (2021-04-21T16:43:36+00:00) The React Context hell. Retrieved from https://www.scien.cx/2021/04/21/the-react-context-hell/

MLA
" » The React Context hell." Alfredo Salzillo ? | Sciencx - Wednesday April 21, 2021, https://www.scien.cx/2021/04/21/the-react-context-hell/
HARVARD
Alfredo Salzillo ? | Sciencx Wednesday April 21, 2021 » The React Context hell., viewed ,<https://www.scien.cx/2021/04/21/the-react-context-hell/>
VANCOUVER
Alfredo Salzillo ? | Sciencx - » The React Context hell. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/04/21/the-react-context-hell/
CHICAGO
" » The React Context hell." Alfredo Salzillo ? | Sciencx - Accessed . https://www.scien.cx/2021/04/21/the-react-context-hell/
IEEE
" » The React Context hell." Alfredo Salzillo ? | Sciencx [Online]. Available: https://www.scien.cx/2021/04/21/the-react-context-hell/. [Accessed: ]
rf:citation
» The React Context hell | Alfredo Salzillo ? | Sciencx | https://www.scien.cx/2021/04/21/the-react-context-hell/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.