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.
Context.ProviderEvery Context object comes with a Provider React component that allows consuming components to subscribe to context changes. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers.
We can pass in anything we want into the value prop of the context provider component in React. So we can share anything between components as long as we pass them into the value prop.
What is Context API? The React Context API is a way for a React app to effectively produce global variables that can be passed around. This is the alternative to "prop drilling" or moving props from grandparent to child to parent, and so on.
Memoize the Context values with useMemo The simplest way to optimize the Context is to useContext in nested components inside App. js . When Wrap the Context value inside of useMemo, all values will be memoized, and useMemo will only recompute the memoized value when one of the dependencies has changed.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With