meiosis-setup-preact
v1.0.0-beta.1
Published
Meiosis Helpers - setup Preact
Downloads
2
Maintainers
Readme
meiosis-setup
import meiosis from "meiosis-setup/functionPatches";
import simpleStream from "meiosis-setup/simple-stream";
const app = {
initial: { ... },
Actions: (update, getState) => ...,
services: [...],
Effects: (update, actions) => [...]
};
const { update, states, actions } = meiosis({ stream: simpleStream, app });
Meiosis is a pattern, not a library. Nevertheless, in response to popular demand and for your convenience, here are some utility functions that help set up and use Meiosis.
meiosis-setup
wires up the Meiosis pattern for you as explained in
Services and Effects, plus these
conveniences:
- works with Flyd, Mithril-Stream, Mergerino, Function Patches, and Immer out of the box
- provides the ability to call
update()
with an array of patches, automatically combining them into a single patch - provides a simple stream implementation
- provides TypeScript support
- provides convenience functions to wire up React and Preact
- shows how to wire up Mithril and lit-html.
Meiosis works with other view libraries of course.
See the repository for examples:
Also see the Meiosis examples for more examples using
meiosis-setup
.
Getting Started
To use meiosis-setup
, specify a stream library, and, optionally, a library instance to manage how
patches are merged to the application state. Of course, you'll also need a view library.
Choosing a stream library
Out of the box, meiosis-setup
supports these stream libraries:
- the
simpleStream
provided bymeiosis-setup
- Flyd
- Mithril-Stream
You can also use another stream library; see Using another stream library.
Choosing how to merge patches
Remember that the core of the Meiosis pattern is a stream of states
that scan
s an update
stream of patches and merges them to produce the states.
With meiosis-setup
, you can use:
You can also use another strategy of your choice to merge patches. See Common Setup for details.
Choosing a view library
You can use any view library that fits with the Meiosis pattern. meiosis-setup
provides helper
functions to use React and Preact. You can also use
Mithril and lit-html without any
special setup. See View Setup for details.
Installation
Using npm
:
npm i meiosis-setup
Using a script
tag:
<script src="https://unpkg.com/meiosis-setup"></script>
Using the script
tag exposes a Meiosis
global, under which the helper functions are
provided:
mergerino.setup
functionPatches.setup
immer.setup
common.setup
preact.setup
react.setup
simpleStream.stream
simpleStream.scan
Meiosis Setup
The setup
function sets up the Meiosis pattern using the stream library and application that you
provide. In the application, you can define the initial
state, the Actions
constructor function,
the array of services
, and the Effects
constructor function, all of which are optional.
The setup
function returns the update
and states
streams, as well as the created actions
.
Application
In the app
object that you pass to setup
, you can optionally provide the following:
initial
: an object that represents the initial state. If not provided, the initial state is{}
.Actions
: a function that receives(update, getState)
and returns an object with actions. The created actions are returned bysetup
, and also passed toEffects
. If not provided, the created actions are{}
. For convience, actions can pass arrays of patches toupdate
to combine multiple patches into one, thus reducing the number of updates, state changes, and view refreshes. If an action is defined withfunction() { ... }
rather than() => { ... }
, it can call another action usingthis.otherAction(...)
.services
: an array of functions that get called withstate
. Service functions can change the state by returning a patch:- returning any patch, changes the state
- not returning anything, or returning a falsy value, does not change the state
Effects
: constructor function for effects, which gets called with(update, actions)
and should return an array of functions that will get called withstate
. The return value of effect functions is ignored. Instead, effect functions should callupdate
and/oractions
to trigger further updates.
For an explanation of services and effects, please see the Services and Effects documentation.
Mergerino Setup
To use Mergerino:
import meiosis from "meiosis-setup/mergerino";
import simpleStream from "meiosis-setup/simple-stream";
// or
// import Stream from "mithril/stream";
// or
// import flyd from "flyd";
import merge from "mergerino";
// A) if the initial state is synchronous:
const app = { initial: ..., ... };
const { update, states, actions } =
meiosis({ stream: simpleStream, merge, app });
// setup your view here
// call update({ duck: "quack" }) to update the state
// and/or call actions.someAction(someValue)
// OR
// B) if the initial state is asynchronous:
asyncFunction(...).then(response => {
const initial = buildInitialState(response);
const app = { initial, ... };
meiosis({ stream: simpleStream, merge, app });
// setup your view here
// call update({ duck: "quack" }) to update the state
// and/or call actions.someAction(someValue)
});
Function Patch Setup
To use Function Patches:
import meiosis from "meiosis-setup/functionPatches";
import simpleStream from "meiosis-setup/simple-stream";
// or
// import Stream from "mithril/stream";
// or
// import flyd from "flyd";
// A) if the initial state is synchronous:
const app = { initial: ..., ... };
const { update, states, actions } =
meiosis({ stream: simpleStream, app });
// setup your view here
// call update(state => ({ ...state, duck: "quack" })) to update the state
// and/or call actions.someAction(someValue)
// OR
// B) if the initial state is asynchronous:
asyncFunction(...).then(response => {
const initial = buildInitialState(response);
const app = { initial, ... };
meiosis({ stream: simpleStream, app });
// setup your view here
// call update(state => ({ ...state, duck: "quack" })) to update the state
// and/or call actions.someAction(someValue)
});
Immer Setup
To use Immer:
import meiosis from "meiosis-setup/immer";
import simpleStream from "meiosis-setup/simple-stream";
// or
// import Stream from "mithril/stream";
// or
// import flyd from "flyd";
import produce from "immer";
// A) if the initial state is synchronous:
const app = { initial: ..., ... };
const { update, states, actions } =
meiosis({ stream: simpleStream, produce, app });
// setup your view here
// call update(draft => { draft.duck = "quack"; }) to update the state
// and/or call actions.someAction(someValue)
// OR
// B) if the initial state is asynchronous:
asyncFunction(...).then(response => {
const initial = buildInitialState(response);
const app = { initial, ... };
meiosis({ stream: simpleStream, produce, app });
// setup your view here
// call update(draft => { draft.duck = "quack"; }) to update the state
// and/or call actions.someAction(someValue)
});
View Setup
Mithril Setup
To use meiosis-setup
with Mithril, no special setup is necessary:
import meiosis from "meiosis-setup/...";
import m from "mithril";
const App = {
// If you only use update or actions, you can omit the other
view: ({ attrs: { state, update, actions } }) => m(...)
};
// If you only use update or actions, you can omit the other
const { states, update, actions } = meiosis({ ... });
m.mount(document.getElementById("app"), {
// If you only use update or actions, you can omit the other
view: () => m(App, { state: states(), update, actions })
});
states.map(() => m.redraw());
See here for an example.
React Setup
To create the top-level App
component with React, use:
import meiosis from "meiosis-setup/...";
import reactSetup from "meiosis-setup/react";
import React from "react";
import ReactDOM from "react-dom";
// your root component
const Root = ({ state, update, actions }) => (
<div>...</div>
);
const App = reactSetup({ React, Root });
// Actions are optional
const app = { initial, Actions, ... };
// If you only use update or actions, you can omit the other
const { state, update, actions } = meiosis({ stream, app, ... });
const element = document.getElementById("app");
// If you only use update or actions, you can omit the other
ReactDOM.render(<App states={states} update={update} actions={actions} />, element);
See here for an example.
Preact Setup
To create the top-level App
component with Preact, use:
import meiosis from "meiosis-setup/...";
import preactSetup from "meiosis-setup/preact";
import { h, render } from "preact";
import { useState } from "preact/hooks";
// your root component
const Root = ({ state, update, actions }) => (
<div>...</div>
);
const App = preactSetup({ h, useState, Root });
// Actions are optional
const app = { initial, Actions, ... };
// If you only use update or actions, you can omit the other
const { state, update, actions } = meiosis({ stream, app, ... });
const element = document.getElementById("app");
// If you only use update or actions, you can omit the other
render(<App states={states} update={update} actions={actions} />, element);
See here for an example.
lit-html Setup
To use meiosis-setup
with lit-html, no special setup is
necessary:
import meiosis from "meiosis-setup/...";
import { html, render } from "lit-html";
const App = ({ state, update, actions }) => html`
<div>... </div>
`;
// Actions are optional
const app = { initial, Actions, ... };
// If you only use update or actions, you can omit the other
const { state, update, actions } = meiosis({ stream, app, ... });
const element = document.getElementById("app");
// If you only use update or actions, you can omit the other
states.map(state => render(App({ state, update, actions }), element));
See here for an example.
Common Setup
For a setup other than the supported libraries, you can use meiosis-setup/common
. All you need to
do is specify the accumulator
function and the combine
function:
accumulator
:f(state, patch) => updatedState
. This function gets the latest state and the patch (the patch being in whatever form you decide to use), and returns the updated state.With Mergerino, the
accumulator
ismerge
.With Function Patches, the
accumulator
is(state, patch) => patch(state)
.With Immer, the
accumulator
isproduce
.combine
: thecombine
function is of the form([patches]) => patch
, combining an array of patches into a single patch.With Mergerino:
combine = patches => patches
With Function Patches:
combine = fns => args => fns.reduce((arg, fn) => fn(arg), args)
With Immer,
combine = patches => state => patches.reduce((result, patch) => produce(result, patch), state)
. We can't usepatches.reduce(produce, state)
because that would send a third argument toproduce
and not work correctly.
Using another stream library
You can use another stream library, as long as you provide either a function that creates a stream,
or an object with a stream
property for that function. In either case, there must also be a scan
property on the function or object. Finally, the created stream must be a function that, when
called, emits a value onto the stream; and the function must have a map
method.
Nesting
As explained in the Components section of the Meiosis Tutorial, we can use IDs for using multiple instances of a component. Optionally, we can also use nesting.
With nesting, we call a helper function nest
with a path to where the component state is stored
within the top-level application state. The nest
function returns a local
object that contains
these functions:
get(<state>)
: returns the component's local state from the top-levelstate
patch(<patch>)
: creates a top-level patch from the component's local patch
By passing local
to a component, the component can use get
and patch
to respectively get and
update its local state without needing to know where its state is stored within the top-level
application state.
To use nesting, start by obtaining the nest
function according to the type of patch:
const nest = meiosis.functionPatches.nest;
const nest = meiosis.mergerino.nest;
const nest = meiosis.immer.nest(produce);
Then call nest
, passing the path, either a string for a single level, or an array of strings for
nesting down multiple levels:
const local = nest("conditions");
const local = nest(["temperature", "air"]);
Finally, pass local
to the component, and use local.get
and local.patch
to get and update the
local state:
const temperature = local.get(state).value;
update( local.patch({ value: x => x + amount }) );
See the repository for examples:
API
Credits
Many thanks to Barney Carroll, James Forbes, Daniel Loomer, Scotty Simpson, and Stephan Thon for your contributions, feedback, and suggestions. They are much appreciated!
meiosis-setup is developed by foxdonut (@foxdonut00) and is released under the MIT license.