react-apothecary
v0.5.2
Published
React UI bindings for apothecary
Downloads
10
Readme
React-Apothecary
React bindings for the apothecary state
container. Use this to communicate with the apothecary
strore.
Usage
The core API consists of two elements: Bridge
and tunnel
.
Bridge
Normally, your whole app will be wrapped in this component. It provides the apothecary
store to a component tree:
import React from "react";
import { initialize } from "apothecary";
import { Bridge } from "react-apothecary";
import App from './App';
const store = initialize({ n: 1 });
export default () => <Bridge store={store}><App /></Bridge>;
tunnel
Use this HOC to connect your component to the apothecary
store. The first argument is
used to inject properties from the store into your component. The second argument can be
used to inject mutators, which react-apothecary
will automatically bind to the dispatch
.
Example:
import React from "react";
import { split } from "apothecary";
import { tunnel } from "react-apothecary";
const increment = () => split(n => n + 1, "n");
const decrement = () => split(n => n - 1, "n");
function Counter({ n, inc, dec }) {
return (
<div>
<button onClick={dec}>-</button>{n}<button onClick={inc}>+</button>
</div>
);
}
export default tunnel(state => ({ n: state.n }), {
inc: increment,
dec: decrement
})(Counter);
Let's examine each argument to tunnel
in detail.
First argument: select(state, inputProps) -> outputProps
The first argument is a function that you use to inject specific pieces of your
application state into a component. In the example above, we are injecting the n
variable from our application state as a prop to our Counter
component.
The select
function also accepts the incoming component props as a second argument.
Consider this example:
export default tunnel((state, inputProps) => ({
n: state.n + inputProps.offset || 0
}))(Counter);
Now if we were to render our Counter
component with the offset
prop, it would change
the value of n
:
<Counter offset={3} />
Assuming n
is equal to 1
in the initial state, our component will render with an n
equal to 4
.
Second argument: mutators
The second argument is for injecting apothecary
mutators into your component. The tunnel
HOC will take
care of binding them to the dispatch
, so that your component doesn't need to keep a
reference to the dispatch
. This means that calling the function from the component will
result in a state change in the store. There is a standard and a more advanced way to use this,
both described below.
Standard Usage: Object Literals
Normally we use a flat object literal made up of higher order apothecary
mutators. Let's look at the original example again:
const increment = () => split(n => n + 1, "n");
const decrement = () => split(n => n - 1, "n");
export default tunnel(state => ({ n: state.n }), {
inc: increment,
dec: decrement
})(Counter);
In this case our Counter
component will receive inc
and dec
props. When these are executed, they will change the value of n
in the store.
Advanced Usage: Using fromProps(inputProps, outputProps, state) -> mutators
A second way to specify the mutators is to use the fromProps
function. Consider this example:
import { split } from 'apothecary';
import { fromProps } from 'react-apothecary';
const increment = step => () => split(n => n + step, "n");
const decrement = step => () => split(n => n - step, "n");
export default tunnel(state => ({ n: state.n }), fromProps(props => ({
inc: increment(props.step),
dec: decrement(props.step)
})))(Counter);
In this case, we'd render the component with a step
prop that indicates how much n
should
change by when we execute our mutators:
<Counter step={5} />
In addition to the input props, the function that you pass into fromProps
also accepts
two more arguments. Here are all the arguments described in order:
inputProps
: the props passed into the component by the parentoutputProps
: the props that were computed as a result of theselect
functionstate
: the rawapothecary
state.
These are provided in case they are helpful in the act of composing your mutators. Most of the time, the first argument should be sufficient.
Contributions
TBD
Testing
TBD