react-insula
v2.1.6
Published
`react-insula` allows React components to be directly connected to [Insula](https://github.com/chandlerprall/insula) stores, allowing easy state transformations and subscriptions.
Downloads
6
Readme
react-insula
allows React components to be directly connected to Insula stores, allowing easy state transformations and subscriptions.
Examples
Installation
npm
npm install --save --react-insula
yarn
yarn add react-insula
Creating and attaching state
Before connecting a stateful component, the state has to be exposed to the React DOM tree. This is done by wrapping your application with the Provider
component.
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-insula';
const initialState = {
deeply: {
nested: {
object: 'value'
}
},
foo: 'bar'
};
const store = new Store(initialState);
ReactDOM.render(
<Provider store={store}>
<div>... rest of application goes here ...</div>
</Provider>,
document.getElementById('application')
);
Subscribing components
To pull values from state into a component and have the component update when those values change, wrap the component with the connect
function. This works for both Function
components and Component
classes. Insula uses selectors
to specify which parts of state you want to access.
import React from 'react';
import {connect} from 'react-insula';
function MyComponent({theValue, foo}) {
return (
<div>
theValue: {theValue}
<br/>
foo: {foo}
</div>
);
}
// we want to access two values from the state: `deeply.nested.object` and `foo`
const selectors = [
['deeply', 'nested', 'object'],
['foo']
];
const transformer = ([deeplyNestedObject, foo]) => {
return {
theValue: deeplyNestedObject,
foo
};
};
export default connect(selectors, transformer)(MyComponent);
Transformers
As the above example demonstrates, the first argument to a transformer function is an array with the selected state values. The object returned by a transformer will be merged with any props set by the parent and passed down to the child component.
Accessing props
Sometimes it is useful to access the component's props in the transformer. These props are always passed to the transformer function as an additional value in the array.
Parent.js
function Parent() {
return (
<Child foo="bar" baz={[1,2,3]}/>
);
}
Child.js
export default connect(
[['stateValue']], // one selector
([stateValue, props]) => {
return {
stateValue: props.includeStateValue ? stateValue : null
};
}
)(ChildComponent);
Dispatching events
Insula's state management is event driven so all connected views are also given a dispatch
function that takes an event type and optional payload.
function MyComponent({dispatch, subject, body}) {
const onClick = () => dispatch('SEND_MESSAGE', {subject, body});
return (<button onClick={onClick}>Send</button>);
}
export default connect(
[ ['message', 'subject'], ['message', 'body'] ],
([subject, body]) => ({subject, body})
)(MyComponent);
Bound dispatches
A key philosophy behind react-insula
is that views should not know what is supposed to happen. Instead, they should be given a function that does something at a given interaction, but they don't care what. Transformers are passed an object as the second argument with a bindDispatch
method which allows them to create dispatcher functions. The event payload can optionally be curried onto the dispatch as well.
function MyComponent({resetForm, sendMessage}) {
const onClick = () => dispatch('SEND_MESSAGE', {title: 'Hello', message: 'World'});
return (
<div>
<button onClick={sendMessage}>Send</button>
<button onClick={resetForm}>Reset</button>
</div>
);
}
export default connect(
[ ['message', 'subject'], ['message', 'body'] ],
([subject, body], {bindDispatch}) => {
return {
resetForm: bindDispatch('RESET_FORM'), // no payload defined, the view could optionally add one
sendMessage: bindDispatch('SEND_MESSAGE', {subject, body}) // payload is defined, the view can't do anything but call the function
};
}
)(MyComponent);
Runtime event listeners
While event listeners can be added to the store directly, sometimes an event only makes sense in the context of a component, or maybe a mounted component needs to Do Something Else in response to an event. These runtime event listeners can be specified by passing a configuration object to connect
.
export default connect(
[[]],
([state]) => ({}),
{
listeners: {
SEND_MESSAGE: (payload, {dispatch}) => {
const {subject} = payload;
dispatch('RECORD_ANALYTICS', {eventType: 'sentMessage', subject});
}
}
}
)(MyComponent);
The second parameter passed to the listener function provides the dispatch
, getState
, getPartialState
, setState
, and setPartialState
methods from the Insula store.
Runtime event listeners are added during the componentDidMount
lifecycle and removed at componentWillUnmount
. Additionally, the listener's this
context is your component, allowing you to access the instance methods.
class MyComponent extends Component {
reactToEvent() {
// ... do something
}
render() {return <div/>;}
}
export default connect(
[[]],
([state]) => ({}),
{
listeners: {
'EVENT_TYPE': () => {
this.reactToEvent();
}
}
}
)(MyComponent);