Wrap providers elegantly using withProvider HoC
Tags: react, tutorial, hoc
Published: 2020-07-18 21:20:25

---
title: Wrap providers elegantly using withProvider HoC
description: If you use React contexts, you probably have created a wrapper component before, which includes just the context Provider.
published: true
date: 2020-07-12 00:00:00 UTC
tags: react, tutorial, hoc
canonical_url: https://maximzubarev.com/wrap-providers-using-withprovider-hoc
---

If you use React contexts, you probably have created a wrapper component before, which includes just the context Provider. You do that because you need the context data inside some component, but for it to be available, the Provider can't be rendered in the same component. It needs to be rendered somewhere above.

Here is a pattern that I use from time to time. It allows me to work with React contexts in a very compressed manner (I mean in the same file). Let's look at an example:

```jsx
import React, { useContext, useState } from "react";

const CounterContext = React.createContext();

const CounterProvider = ({ children }) => {
  const [count, setCount] = useState(0);

  return (
    <CounterContext.Provider
      value={{
        count,
        increment: () => setCount((prevCount) => prevCount + 1),
      }}
    >
      {children}
    </CounterContext.Provider>
  );
};

const App = () => {
  const { increment, count } = useContext(CounterContext);

  return (
    <div>
      <h2>{count}</h2>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

// 👇 without this 🤡, useContext in App will return undefined.
const WrappedApp = () => (
  <CounterProvider>
    <App />
  </CounterProvider>
);

export default WrappedApp;
```

If we create a simple higher-order component, we can use that instead of arbitrarily creating provider wrappers like `WrappedApp`.

```jsx
const withBasicProvider = (Provider) => (Component) => (props) => (
  <Provider>
    <Component {...props} />
  </Provider>
);

// ...

export default withBasicProvider(CounterProvider)(WrappedApp);
```

This is a very naive implementation and will simply wrap a component with the passed argument, so you can omit creating a provider wrapper component manually.

But what if we have more than one context, which we want to use? We can `reduce` any number of given providers to incrementally wrap the passed component.

I'm using `reduceRight` just, to preserve the order of passed providers (e.g. the `CounterProvider` wraps `DarkModeProvider` wraps `App`):

```jsx
export const withBasicProviders = (...providers) => (WrappedComponent) => (props) =>
  providers.reduceRight((acc, Provider) => {
    return <Provider>{acc}</Provider>;
  }, <WrappedComponent {...props} />);

// somewhere here `DarkModeProvider` has been added, too.
// It really doesn't matter how it looks in detail. This
// HoC is just about handling providers. In the end there
// is a CodeSandbox link for you to play around with
// actually working code.

// ...

export default withBasicProviders(CounterProvider, DarkModeProvider)(App);
```

Now, to make this HoC truly useful, let's see how to pass props to their respective providers. I am sure you can implement that functionality in various ways, but I feel the following is quite neat.

If there are any props that you want a provider to receive, you can simply pass an array. The HoC will check if the current provider item is an array. If it is, `withProviders` will use the object, that the second item is supposed to be, as props. If it isn't, it behaves just like withBasicProviders before - it just wraps the previous element.

```jsx
export const withProviders = (...providers) => (WrappedComponent) => (props) =>
  providers.reduceRight((acc, prov) => {
    let Provider = prov;
    if (Array.isArray(prov)) {
      Provider = prov[0];
      return <Provider {...prov[1]}>{acc}</Provider>;
    }

    return <Provider>{acc}</Provider>;
  }, <WrappedComponent {...props} />);

// ...

export default withProviders([CounterProvider, { start: 5 }], DarkModeProvider)(App);
```

If you want to play around with some working code, here's a [CodeSandbox demo](https://codesandbox.io/s/withproviders-2tdtr).

(This is an article posted to my blog at [maximzubarev.com](https://maximzubarev.com). You can read it online by [clicking here](https://maximzubarev.com/wrap-providers-using-withprovider-hoc).)