meiosis-setup-preact

1.0.0-beta.1 • Public • Published

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:

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:

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 scans 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 by setup, and also passed to Effects. If not provided, the created actions are {}. For convience, actions can pass arrays of patches to update to combine multiple patches into one, thus reducing the number of updates, state changes, and view refreshes. If an action is defined with function() { ... } rather than () => { ... }, it can call another action using this.otherAction(...).
  • services: an array of functions that get called with state. 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 with state. The return value of effect functions is ignored. Instead, effect functions should call update and/or actions 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 is merge.

    With Function Patches, the accumulator is (state, patch) => patch(state).

    With Immer, the accumulator is produce.

  • combine: the combine 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 use patches.reduce(produce, state) because that would send a third argument to produce 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-level state
  • 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

API documentation is here.

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.

Package Sidebar

Install

npm i meiosis-setup-preact

Weekly Downloads

3

Version

1.0.0-beta.1

License

MIT

Unpacked Size

21.4 kB

Total Files

6

Last publish

Collaborators

  • foxdonut