Providing and consuming context

As your React application grows, it will use more components. Not only will it have more components, but the structure of your application will change so that components are nested more deeply. The components that are nested at the deepest level still need to have data passed to them. Passing data from a parent component to a child component isn't a big deal. The challenge is when you have to start using components as indirection for passing data around your app.

For data that needs to make its way to any component in your app, you can create and use a context. There are two key concepts to remember when using contexts in React—providers and consumers. A context provider creates data and makes sure that it's available to any React components. A context consumer is a component that uses this data within the context.

You might be wondering whether or not context is just another way of saying global data in a React application. Essentially, this is exactly what contexts are used for. Using the React approach to wrapping components with a context works better than creating global data because you have better control of how your data flows down through your components. For example, you can have nested contexts and a number of other advanced use cases. But for now, let's just focus on simple usage.

Let's say that you have some application data that determines permissions for given application features. This data could be fetched from an API or it could be hardcoded. In either case, the requirement is that you don't want to have to pass all of this permission data through the component tree. It would be nice if the permission data were just there, for any component that needs it.

Starting at the very top of the component tree, let's look at index.js:

import React from 'react';
import { render } from 'react-dom';

import { PermissionProvider } from './PermissionContext';
import App from './App';

render(
<PermissionProvider>
<App />
</PermissionProvider>,
document.getElementById('root')
);

The <App> component is the child of the <PermissionProvider> component. This means that the permission context has been provided to the <App> component and any of its children, all the way down the tree. Let's take a look at the PermissionContext.js module where the permission context is defined:

import React, { Component, createContext } from 'react';

const { Provider, Consumer } = createContext('permissions');

export class PermissionProvider extends Component {
state = {
first: true,
second: false,
third: true
};

render() {
return (
<Provider value={this.state}>{this.props.children}</Provider>
);
}
}

const PermissionConsumer = ({ name, children }) => (
<Consumer>{value => value[name] && children}</Consumer>
);

export { PermissionConsumer };

The createContext() function is used to create the actual context. The return value is an object containing two components—Provider and Consumer. Next, there's a simple abstraction for the permission provider that's to be used all throughout the app. The state contains the actual data that components might want to use. In this example, if the value is true, the feature should be displayed as normal. If it's false, then the feature doesn't have permission to render. Here, the state is only set once, but since this is a regular React component, you could set the state the same way you would set the state on any other component. The value that's rendered is the <Provider> component. This provides any children with context data, set via the value property.

Next, there's a small abstraction for permission consumers. Instead of having every component that needs to test for permissions implement the same logic over and over, the PermissionConsumer component can do it. The child of the <Consumer> component is always a function that takes the context data as an argument. In this example, the PermissionConsumer component has a name prop, for the name of the feature. This is compared with the value from the context and if it's false, nothing is rendered.

Now let's look at the App component:

import React, { Fragment } from 'react';

import First from './First';
import Second from './Second';
import Third from './Third';

export default () => (
<Fragment>
<First />
<Second />
<Third />
</Fragment>
);

This component renders three components that are features and each need to check for permissions. Without the context functionality of React, you would have to pass this data as properties to each of these components through this component. If <First> had children or grandchildren that needed to check permissions, the same property-passing mechanism can get quite messy.

Now let's take a look at the <First> component (the <Second> and <Third> are almost exactly the same):

import React from 'react';
import { PermissionConsumer } from './PermissionContext';

export default () => (
<PermissionConsumer name="first">
<div>
<button>First</button>
</div>
</PermissionConsumer>
);

This is where the PermissionConsumer component is put to use. You just need to supply it with a name property, and the child component is the component that is rendered if the permission check passes. The <PermissionConsumer> component can be used anywhere, and there's no need to pass data in order to use it. Here's what the rendered output of these three components looks like:

The second component isn't rendered because its permission in the PermissionProvider component is set to false.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.46.36