Context Wrapper
It is a good practice that our context is not just a plain object but it has an interface that allows us to store and retrieve data. For example:
Then, if we go back to our example, the very top App component may look like that:
And our Title component gets it's data through the context:
Ideally we don't want to specify the contextTypes every time when we need an access to the context. This detail may be wrapped in a higher-order component. And even more, we may write an utility function that is more descriptive and helps us declare the exact wiring. ie. instead of accessing the context directly with this.context.get('title') we ask the higher-order component to get what we need and to pass it as a prop to our component. For example:
The wire function accepts first a React component, then an array with all the needed dependencies (which are registered already) and then a function which I like to call mapper. It receives what's stored in the context as a raw data and returns an object which is the actual React props for our component (Title). In this example we just pass what we get - a title string variable. However, in a real app this could be a collection of data stores, configuration or something else. So, it's nice that we pass exactly what we need and don't pollute the components with data that they don't need.
Here is how the wire function looks like:
Inject is a higher-order component that gets access to the context and retrieves all the items listed under dependencies array. The mapper is a function receiving the context data and transforms it to props for our component.
Non-context alternative
Use a singleton to register/fetch all dependencies
We'll store the dependencies in dependencies global variable (it's global for our module, not at an application level).
We then export two functions register and fetch that write and read entries.
It looks a little bit like implementing setter and getter against a simple JavaScript object.
Then we have the wire function that accepts our React component and returns a higher-order component.
In the constructor of that component we are resolving the dependencies and later while rendering the original component we pass them as props.
We follow the same pattern where we describe what we need (deps argument) and extract the needed props with a mapper function.
Having the di.jsx helper we are again able to register our dependencies at the entry point of our application (app.jsx) and inject them wherever (Title.jsx) we need.
If we look at the Title.jsx
file we'll see that the actual component and the wiring may live in different files. That way the component and the mapper function become easily unit testable.
Related links:
Last updated
Was this helpful?