Different Ways of Getting React Context Value

2020-04-14 • 3 min read
#react#context

Recently I am refactoring our code base for extracting out similar logic to React Context. Since our code base has been maintained by several developers over the years, there are a lot of different kinds of coding style. I would like to write a post to note down how to use React Context value in different situations.

Suppose we have a context that looks like the following:

import React from 'react';

export const MyContext = React.createContext({
  someValue: undefined,
});

export const MyContextProvider = ({
  children,
}) => {

  const value = { someValue: 'someValue' };

  return (
    <MyContext.Provider value={value}>
      {children}
    </MyContext.Provider>
  );
};

export const useMyContext = () => {
  const context = React.useContext(MyContext);

  if (!context) {
    throw new Error(
      'useMyContext must be used within a MyContextProvider'
    );
  }
  return context;
};

Class Component

First, in class component, we could write it like what the official document suggests:

import { MyContext } from './myContext';

class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let { someValue } = this.context;
    /* render something based on someValue */
  }
}

React Hooks

Or if you are writing functional component, you could use React Hooks.

import { useMyContext } from './myContext';

const MyComponent = () => {
  const { someValue } = useMyContext();
  return (/* render something based on someValue */);
};

Recompose

In our code base, there are components using recompose. At first, I think it's functional component because of its written style. Yet, we cannot use hooks directly inside it.

And... I found another way to use context inside this kind of component, called fromRenderProps.

import React from 'react';
import { fromRenderProps } from 'recompose';
import { MyContext } from './myContext';

const enhance = 
  fromRenderProps(MyContext.Consumer, ({ someValue }) => ({ someValue }));

const MyComponent = enhance(({ someValue }) => {
  return (/* render something based on someValue */);
};

createReactClass

If you are migrating from React 15 to React to 16, your code base might use create-react-class for adopting React 16. Similar to class component, you will need this.context. The setup is a bit different though.

import createReactClass from 'create-react-class';
import { MyContext } from './myContext';

const MyComponent = createReactClass({
  render() {
    const { someValue } = this.context;
    return (/* render something based on someValue */);
  }
});

MyComponent.contextType = MyContext;

ReasonReact

Bonus part! If you are using ReasonReact, they provide how to get context value in their document.

/* ContextProvider.re */
let themeContext = React.createContext("light");

let makeProps = (~value, ~children, ()) => {
  "value": value,
  "children": children,
};

let make = React.Context.provider(themeContext);

/* ConsumerComponent.re */
[@react.component]
let make = () => {
  let theme = React.useContext(ContextProvider.themeContext);

  <h1>theme->React.string</h1>
}

If I encounter more ways to get context value, I will update this post :D