0.1.12 • Published 7 years ago

react-plumbing v0.1.12

Weekly downloads
47
License
SEE LICENSE IN LI...
Repository
github
Last release
7 years ago

react-plumbing

React-plumbing is an Event propagation a State management framework for react, similar to flux etc.

The services provided by react-plumbing are provided by a container object that wraps your highest-level react object. The Pump object in the react-plumbing framework provides a 'store' object that ecapsulates all application state. All updates to this state should be mediated by the store's action dispatcher. The pump object also takes care of propagating change events to lower-level components via the popagator object. The application data model is exposed the Pump via an object (Site in the example below) that is provided in the Pump's constructor.

import App from './App';
import { Pump, Action, Container } from 'react-plumbing'
import Site from './model/Site'

const pump = new Pump(new Site());

ReactDOM.render(
    <Container 
        store={pump.store} 
        propagator={pump.propagator}>
        <App />
    </Container>, 
    document.getElementById('root'));

React components are wrapped by a HOC so that the store and propagator objects provided to the container are always available:

import { Selector, Action, wrap } from 'react-plumbing'
import { Modal, Button, Panel, Accordion } from 'react-bootstrap'

const site = Action.builder;

function MessagePopup(props, context) {

    function formatMessage(message) { return ( 
        <Panel key={message.id} header={message.text}>{message.detail}</Panel>
    );}

    return (
        <Modal.Dialog>
            <Modal.Header><Modal.Title>Alert</Modal.Title></Modal.Header>
            <Modal.Body> 
                <Accordion>{ props.messages.map(formatMessage) }</Accordion>
            </Modal.Body>
            <Modal.Footer>
                <Button onClick={context.submitter(site.clearMessages())}>OK</Button>
            </Modal.Footer>
        </Modal.Dialog>
    );
}

export default wrap(MessagePopup,  Selector.map( site => { return { messages: site.messages } } ));

In the above example, the Action API is being used to prepare actions to send to the Store via the context.submitter function, and theSelector API is being used to filter information from the Site object and preset it to the JSX view. Action allows you to reference methods and properties of the site object in order to construct complex actions fairly freely:

const site = Action.builder;

function loginAction(form) {
    return site.clearMessages()
        .then(site.authenticate(form.username,form.password))
        .then(site.closePopup().when(site.authenticated));
}

In the above example the loginAction function is creating an action that will call the clearMessages method on the actual Site object provided to Pump, then the authenticate method, and then the closePopup method - but only if the authenticated property of site is true. These methods can return ordinary objects or promises.

The important thing here is that creating an action binds any parameters (form.username and password above) but does not invoke the methods. Method invocation happens only when the action is submitted via context.submitter or context.store.submit.

On completion of the action (including resolving any promises), changes to the site object are broadcast to all components. The Selector object in the above example ensures this the dialog is aware of any changes to the messages property of site.

For this to work, Site (and any sub-objects) need to be immutable. That is, 'setter' methods need to be written to return a new copy of the object (or a promise thereof) rather than change the original. For example, the clearMessages method above would be written:

clearMessages() {
    return Object.assign({}, this, { messages: [] });
}

Action takes care of some of the awkwardness of this when invoking methods on sub-objects of site. For example:

const action = site.screen.cursor.next();

Will ultimately perform something like the following method chain when submitted to the store:

let new_cursor = store.site.screen.cursor.next();
let new_screen = store.site.screen.setCursor(new_cursor);
store.site = store.site.setScreen(new_screen);

Although since the store also takes care of resolving any promises the actual code is somewhat more complicated.

0.1.12

7 years ago

0.1.11

7 years ago

0.1.10

7 years ago

0.1.9

7 years ago

0.1.8

7 years ago

0.1.6

7 years ago

0.1.5

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago