You may have come across React’s Context API without realizing it. Same goes for Higher-Order-Components.

If you have ever worked with Redux, you would have experienced both, React’s Context API (under the hood) and Higher-Order-Component, { connect} from their react-redux package. Or @material-ui/core with their withStyles

Both of these concepts are not very difficult but I think realizing when to use them is the difficult part.

In the application I was working on, I had a client object that was used to store system wide defaults and settings as well as reusable objects (the application I was building also had the concept of a middlware so the objects within it could change based on the middlewares injected)

Our scenario

We will be working on the “theme” for our application. We have an object that stores our default theme which our pages can use.

This scenario can be built entirely with React’s Context API.

Context API

Let’s get started by create a file called ThemeContext.js

import React from 'react'; export default React.createContext();

In our application entry point, we want to wrap our child renders with the following:

import React from 'react';

import ThemeContext from './ThemeContext'; const App = () => { return (

<ThemeContext.Provider value="dark"> <Welcome /> </ThemeContext.Provider> )

} ReactDOM.render(<App />, document.getElementById("root"));

and in our Welcome component:

import React from 'react';

import ThemeContext from './ThemeContext'; const Welcome = () => {

<ThemeContext.Consumer>

{theme => {

return (

<div style={{background: theme === "dark" ? "#000" : "#fff"}}>

<h2 style={{color: theme !== "dark" ? "#000" : "#fff"}}>Welcome</h2>

</div>

)

}}

</ThemeContext.Consumer>

} export default Welcome;

This demonstrates the simplicity of the React’s Context API. We first defined our Context API. We define the provider within our App component here as this will ensure that the context is provided to all of the children once needed.

I think that is something you should be aware of, if the provider isn’t defined higher in the tree where the consumer sits, then you will never be able to get the values from within.

Next we define our consumer, here we can access our context value theme which we can use to customize our components.

This example isn’t technically difficult and you can get away with just passing in theme as prop in this case. However, you need to imagine that in a large application, you could have dozens of components all needed access to this value someway or another.

This is fun right?

But where does Higher-Order-Components come into this?

Higher-Order Components

Personally I hate the syntax used for using the API Context Consumer and it can get quiet tiresome. Also if my theme object was complex, I may not need all of the values from it so I want the ability to pick and choose when I need them.

Let say my theme object looked like the following:

const theme = {

background: {

color: {

primary: "#f00",

secondary: "#f0f"

}

} ...// more items here

};

If I needed to access anything here, my components render will start to get really messy. What makes it worse is if you had another Context you were trying to access. Nesting these will be horrible.

So let’s create our Higher-Order Component.

import React from "react";

import ThemeContext from "./ThemeContext"; const defaultMergeProps = (themeProps) => ({

...themeProps

}); export const connect = (mergeProps = defaultMergeProps) => ComponentToWrap => {

return class ClientComponent extends React.Component {

render() {

return (

<ThemeContext.Consumer>

{theme => {

const props = {...mergeProps({ theme }), ...this.props};

return <ComponentToWrap {...props} />;

}}

</ThemeContext.Consumer>

);

}

};

}; export default connect;

Here, we are creating our HOC, called connect . This takes in a function to transform the properties passed in to our component. This also returns a function that takes in our component and injects the properties to it.

Let’s break it down a little further.

We have our defaultMergeProps function which take in the theme props. The default functionality is to just return the values of the theme object.

Now you can see that in the returned function, we use the Context API (our ThemeContext) consumer and pass in the theme object down to the mergeProps function. This allows us to customize the way we use extract out the properties.

Let’s use our connect property and see the benefits of it.

import React from 'react'; import { connect } from './connect'; const Welcome = ({primaryBackground, primaryText}) => {

return (

<div style={{background: primaryBackground}}>

<h2 style={{color: primaryText}}>Welcome</h2>

</div>

)

} const mergeProps = (theme) => (

{

primaryBackground: theme.background.primary,

primaryText: theme.text.color.primary

}

); export default connect(mergeProps)(Welcome);

AS you can see, the component itself is much neater. We extracted out the properties we needed from the theme props and injected them to our Component like they are regular properties.

As you can see, these two patterns have their advantages but I wouldn’t go wild. More than likely, you will not need to be working with them and your needs may be met with packages like Redux. But If you are going to do it, combing the two patterns sure helps keep your components simpler and clearer to read.

The provider

If you are developing this a package consumed by different teams and possibility multiple projects, I would also create a separate Provider component that deals with the actual Context Provider, which takes in the theme as a property.

import React from "react"; import ThemeContext from "./ThemeContext"; export default class Provider extends React.Component { render() { return ( <ThemeContext.Provider value={this.props.theme}> {this.props.children} </ThemeContext.Provider> ); } }

And in our App.js, we can hide the underlying implementation with the use of this Provider.

import React from 'react';

import Provider from './Provider'; const App = () => {

let theme = {...}; return (

<Provider theme={theme}> <Welcome /> </Provider> )

} ReactDOM.render(<App />, document.getElementById("root"));

Happy coding!