React JS: Find out if component is inside a context provider

Viewed 7841

I'm using React's Context API to pass some context to lower level components.

I want to be able to run the component without a context provider (for testing). For this to work, I need to check whether there is a context provider around my component.

Example code:

const Wrapper = () => {

  // in my real app, there are some levels 
  // between the provider and the child component

  return <NameProvider value={name: 'User'}>
    <ChildComponent />
  </NameProvider>
}

const ChildComponent = () => {
  if (/* what can I put here ? */) {
    // inside Provider
    return <NameConsumer>
      {context => <span>{context.name}</span>}
    </NameConsumer>
  } else {
    // no provider available, e.g. in a test file
    return <span>Test Text</span>
  }
}

This question is not specifically about testing. There could be other situations where a component needs to work both inside and outside a context provider.

2 Answers

No official way is provided for checking whether there is <Provider> parent for <Consumer> child.

Generally, there is no difference if <Consumer> is inside or outside <Provider> with undefined value, it will be provided with undefined value in both cases.

It's possible to check current context value using internal _currentValue property, but this may result in false positives for undefined context value:

const ChildComponent = () => {
  if (NameConsumer._currentValue !== undefined) {
    // inside Provider
    return <NameConsumer>
      {context => <span>{context.name}</span>}
    </NameConsumer>
  } else {
    // no provider available, e.g. in a test file
    return <span>Test Text</span>
  }
}

Notice that this may not work as expected in asynchronous rendering, and relying on internals isn't recommended. A better way would be to check for test environment instead.

A more testable way is to use NameContext.Consumer consistently instead of NameConsumer, so Consumer property could be mocked in tests. Otherwise this may require to mock a module where NameConsumer is defined.

Just check the context. If it's undefined, then there's no Provider.

const myContext = useContext(theContext);

if ( myContext ) {
  return <div>I have a provider.</div>
}

return <div>I have no provider.</div>

I typically use it for components I want to work standalone and be part of a larger widget. See something like Google Maps where I want to hoist up the map context.

const __mapContext = useContext(MapContext);

if ( __mapContext ) {
  return <GoogleMap {...props} />
}

return (
  <GoogleMapProvider>
    <GoogleMap {...props} />
  </GoogleMapProvider>
);
Related