New React 16.3 Context API


Is it just me, or does the new context API defeat the ease and flexibility of the old context? Now I have to wrap users of context within a consumer component and a wrapper function call which is tightly coupled to the provider. Why can’t I simply have a props-like data structure with an origin of arbitrary anscentry?


In defense of the new stuff, the “tight coupling” I referenced isn’t as bad as it may initially appear (at least to me). For example, I saw instances created via createContext, but in reality it’s a class factory and you can instance those classes to your little heart’s desire. In other words, consumers aren’t tied to a single provider that was created with createContext. The component hierarchy is, in fact, traversed to find the closest instance of a respective provider and uses that for a data source. You can create multiple providers and have consumers pull from any one of those depending on the parenting.

const { Provider, Consumer } = React.createContext();

function MyApp (props) {
  return <div>
    <Provider value="A">
      <MyComponent /> {/* renders A */}
    <Provider value="B">
      <MyComponent /> {/* renders B */}

function MyComponent (props) {
  return <Consumer>{ letter => letter }</Consumer>

This can give you the older style, “arbitrary” provider behavior assuming you only ever use the same provider/consumer classes. But that requirement still makes it more difficult since any redistributable components that may act as consumers since they have no implicit way of knowing what kind of provider is going to be used and thus won’t be able to implement a respective consumer, at least not unless explicitly given one. And at that point we’re back to passing props through children again. But at least it’s only through changes of responsibility.

// ... same MyApp ...

import { LetterRenderer } from 'letter-consumer-lib';

function MyComponent (props) {
  // LetterRenderer may use a consumer internally but
  // won't be linked to the provider in MyApp because
  // it didn't exist when LetterRenderer was defined

  // return <LetterRenderer />

  // instead pass the linked consumer to LetterRenderer

  return <LetterRenderer consumer={ Consumer } />

Where LetterRenderer might look something like:

export function LetterRenderer (props) {
  const Consumer = props.consumer;
  return <Consumer>{ letter => `Your letter is "${letter}"!` }</Consumer>

(though more likely, I guess, the LetterRenderer would take basic props - i.e. letter vs consumer - and transfer them into its own provider-consumer)