> This is a fork of [https://github.com/contiamo/react-connect-context](react-connect-context).
npm install react-connect-context-map> This is a fork of https://github.com/contiamo/react-connect-context.
This module exports a function factory that produces an HOC to wrap a component and provide the value of the context as props to the component, much like the react-redux API. The benefits of this approach versus the traditional Consumer:
* All values are passed as props and can be read in your component's lifecycle methods
* You can trivially export a "pure" version of the component as well as a connected version
* Unlike a Redux container, the connected component can be rendered without being nested in a Provider component
* The factory produces an HOC which can be applied to multiple components
* You can select slices of the context value and map them to props that fit the component API
* You can reference the props passed into the connected component when mapping the context value to props
* You can customize the merging behavior to translate all props fed to the wrapped component
1. yarn add react-connect-context-map
1. At the top of your file, import { connectContext } from "react-connect-context-map"
1. Wrap your component in the function as so: connectContext(Context.Consumer)(MyComponent)
#### Introductory example
``tsx
import React from "react"
import { render } from "react-dom"
import { connectContext } from "react-connect-context-map"
// The shape of the context value
interface ContextType {
color: string
}
// The shape of the props passed to your component
interface MergedPropsType extends ContextType {
children: React.ReactNode
myProp: string
}
// Create a new React Context instance
const Context = React.createContext
// Create an HOC for connecting your component to context
const createContainer = connectContext
// The connected component will receive the props passed in as well as values {children}
// from the context object by default
const Content: React.SFC
I have looked into context and I've seen the color is:
{color}
! I also get my own props like {myProp}!
)
// Call the HOC on the component to connect it to context
const ConnectedContent = createContainer
const App: React.SFC = () => (
// Render the connected components in the provider
render(
document.querySelector("#root")
)
`
#### Mapping context properties to props
This example shows how a context value can be mapped to a different prop name or value.
`tsx
import React from "react"
import { render } from "react-dom"
import { connectContext } from "react-connect-context-map"
interface ContextType {
color: string
}
// The shape of the props that have been derived from context
interface ContextPropsType {
isRed: boolean
}
// The shape of the props that are not derived from context and may be
// passed to the connected component
interface OwnPropsType {
myProp: string
}
// The shape of the props passed to the connected component
interface MergedPropsType extends ContextPropsType, OwnPropsType {}
const Context = React.createContext
const Content: React.SFC This pen {isRed ? "is" : "is not"} red. My prop is {myProp}.
)
// ContextPropsType can be used when the result of mapContextToProps does
// not match the shape of ContextType
const createContainer = connectContext
Context.Consumer,
context => ({
isRed: context.color === "red"
})
)
// OwnPropsType can be used to define the props on the wrapped component
// when additional props not inferred from context are provided
const ConnectedContent = createContainer<
MergedPropsType,
OwnPropsType
>(Content)
render(
document.querySelector("#root")
)
`
#### Customizing prop merging
This example shows how you can customize the merge behavior. Here we even map a non-context prop to another key.
`tsx
import React from "react"
import { render } from "react-dom"
import { connectContext } from "react-connect-context-map"
interface ContextType {
color: string
}
interface OwnPropsType {
theProp: string
}
interface MergedPropsType extends ContextType, OwnPropsType {}
const Context = React.createContext
const Content: React.SFC This pen {isRed ? "is" : "is not"} red. My prop is {theProp}.
)
const createContainer = connectContext
Context.Consumer,
null,
// Merge context properties into props and translate myProp to theProp
(context, props) => ({
...context,
theProp: props.myProp
})
)
const ConnectedContent = createContainer<
MergedPropsType,
OwnPropsType
>(Content)
render(
document.querySelector("#root")
)
`
#### Using non-object context values with mapContextToProps
This example shows how you can allow for non-object context values if you provide a custom mapContextToProps function.
`tsx
import React from "react"
import { render } from "react-dom"
import { connectContext } from "react-connect-context-map"
// Context value is not an object
type ContextType = string
interface ContextPropsType {
color: string
}
interface OwnPropsType {
myProp: string
}
interface MergedPropsType extends ContextPropsType, OwnPropsType {}
const Context = React.createContext
const Content: React.SFC This pen {isRed ? "is" : "is not"} red. My prop is {theProp}.
)
const createContainer = connectContext<
ContextType,
ContextPropsType
>(
Context.Consumer,
// Context value is mapped to an object that conforms to
// ContextPropsType
context => ({ color: context })
)
const ConnectedContent = createContainer<
MergedPropsType,
OwnPropsType
>(Content)
render(
document.querySelector("#root")
)
`
Factory function that creates a container component HOC to consume context Context and map values to match ContextProps.
`tsx
connectContext
// The React Consumer component.
ContextConsumer: React.Consumer
// A function that maps the consumed context value to props to pass to
// the component. The default function requires the context value to be
// an object and maps its properties to component props.
mapContextToProps: MapContextToProps
= context => context,
// A function that merges the props that have been mapped from context
// values with the props passed to the connected component. The default
// function merges context props with the passed props, with the latter
// overwriting the former.
mergeProps: MergeProps
(contextProps, ownProps) => ({ ...contextProps, ...ownProps })
): createContainer // HOC to connect a component.
`
An HOC that returns a connected component that accepts props OwnProps and derives ContextProps to merge into MergedProps. It returns a component that accepts OwnProps as props and renders the given component with MergedProps.
`tsx`
createContainer
// The component to connect.
Component: React.SFC
): React.SFC
A function type that maps the consumed context value Context and props passed to the connected component OwnProps to a subset of the props that can be derived from context ContextProps, to pass to the component.
`tsx
type MapContextToProps
// The consumed context value.
context: Context,
// The props passed to the connected component.
ownProps: OwnProps
) => ContextProps // The props derived from context.
`
A function type that merges the props that have been mapped from context ContextProps with the props passed to the connected component OwnProps to return all the props MergedProps, to pass to the wrapped component.
`tsxmapContextToProps
type MergeProps
// The result of .
contextProps: ContextProps,
// The props passed to the connected component.
ownProps: OwnProps
) => MergedProps // The props to pass to the given component.
`
Sure. Consider using PureComponent or shouldComponentUpdate to let your components know when or when _not_ to update.
Additionally, unlike Redux, React 16.3 allows the creation of _multiple_, composable Contexts, so ideally, you'd be using a Context` that is small enough to house _just the information_ that you'd like to reuse in order to properly separate concerns and correctly use the principle of least privilege when passing context around.
---
Made with ❤️ at Contiamo in Berlin.