Skip to content

Instantly share code, notes, and snippets.

@etienne-dldc
Created March 28, 2018 18:38
Show Gist options
  • Save etienne-dldc/68d9539a42d0a9a2737c44ace26242f8 to your computer and use it in GitHub Desktop.
Save etienne-dldc/68d9539a42d0a9a2737c44ace26242f8 to your computer and use it in GitHub Desktop.
A small function to combine react Contexts.
import React from 'react';
function onlyChild(children) {
return Array.isArray(children) ? children[0] : children;
}
export function combineContext(contexts) {
class Provider extends React.Component {
render() {
const init = this.props.children;
return Object.keys(contexts).reduce((acc, contextName) => {
const TheContext = contexts[contextName];
return <TheContext.Provider value={this.props.value[contextName]}>{acc}</TheContext.Provider>;
}, init);
}
}
class Consumer extends React.Component {
render() {
const init = (value) => onlyChild(this.props.children)(value);
const renderer = Object.keys(contexts).reduce((acc, contextName) => {
const TheContext = contexts[contextName];
return (value) => (
<TheContext.Consumer>
{contextValue => {
return acc({
...value,
[contextName]: contextValue,
});
}}
</TheContext.Consumer>
);
}, init);
return renderer({});
}
}
return {
Consumer,
Provider,
};
}
import * as React from 'react';
import { ConsumerProps, Context, ProviderProps } from 'create-react-context';
function onlyChild(children: any): any {
return Array.isArray(children) ? children[0] : children;
}
export function combineContext<In extends { [key: string]: any }>(
contexts: { [K in keyof In]: Context<In[K]> }
): Context<{ [K in keyof In]: In[K] }> {
class Provider extends React.Component<ProviderProps<{ [K in keyof In]: In[K] }>> {
render() {
const init = this.props.children;
return Object.keys(contexts).reduce((acc, contextName) => {
const TheContext = contexts[contextName];
return <TheContext.Provider value={this.props.value[contextName]}>{acc}</TheContext.Provider>;
}, init);
}
}
class Consumer extends React.Component<ConsumerProps<{ [K in keyof In]: In[K] }>> {
render() {
const init = (value: { [K in keyof In]: In[K] }) => onlyChild(this.props.children)(value);
const renderer = Object.keys(contexts).reduce<(value: { [K in keyof In]: In[K] }) => any>((acc, contextName) => {
const TheContext = contexts[contextName];
return (value: { [K in keyof In]: In[K] }) => (
<TheContext.Consumer>
{contextValue => {
return acc({
...(value as any),
[contextName]: contextValue,
});
}}
</TheContext.Consumer>
);
}, init);
return renderer({} as any);
}
}
return {
Consumer,
Provider,
};
}
@etienne-dldc
Copy link
Author

Hi,

I would appreciate if you had made at complete sentence to ask for an example and some kind of courtesy would have been a plus...
That being said, here is an example of use:

const UserContext = React.CreateContext();
const ThemeContext = React.CreateContext();

const UserAndThemeContext = combineContext({ user: UserContext, theme: ThemeContext });

// somewhere
return (
  <UserAndThemeContext.Provider value={{ user, theme }}>{children}</UserAndThemeContext.Provider>
)

// somewhere else
return (
  <UserAndThemeContext.Consumer>{({ user, theme }) => {
    {/* Do something with user and theme */}
  }}</UserAndThemeContext.Consumer>
)

Note: You don't need to worry about passing a object literal of { user, theme } to the Provider because only the values of the properties are passed to the contexts.

Note 2: The UserAndThemeContext.Consumer will update when any of the context changes so don't use this everywhere 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment