Skip to content

Instantly share code, notes, and snippets.

@foxdonut
Created July 10, 2021 17:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save foxdonut/56eb81e9a1e155c652fe0be287d44fd6 to your computer and use it in GitHub Desktop.
Save foxdonut/56eb81e9a1e155c652fe0be287d44fd6 to your computer and use it in GitHub Desktop.
Meiosis setup with React and Mergerino TS
import React from "react";
import ReactDOM from "react-dom";
import flyd from "flyd";
import merge from "mergerino";
import meiosis, {
Local,
MergerinoPatch,
MergerinoApp,
LocalPatch,
ActionConstructor
} from "../../source/dist";
type Patch = MergerinoPatch<State>;
type TemperaturePatch = MergerinoPatch<Temperature>;
type TemperatureLocal = Local<State, Patch, Temperature, TemperaturePatch>;
type TemperatureUnits = "C" | "F";
interface Temperature {
label: string;
value: number;
units: TemperatureUnits;
}
interface TemperatureActions<P1, P2> {
increment: (local: LocalPatch<P1, P2>, amount: number) => void;
changeUnits: (local: LocalPatch<P1, P2>) => void;
}
interface TemperatureComponent<P1, P2> {
Initial: (label: string) => Temperature;
Actions: ActionConstructor<Temperature, P1, TemperatureActions<P1, P2>>;
}
interface State {
temperature: {
air: Temperature;
water: Temperature;
};
}
interface Attrs {
state: State;
actions: Actions;
}
const nest = meiosis.mergerino.nest;
const convert = (value: number, to: TemperatureUnits): number => {
return Math.round(to === "C" ? ((value - 32) / 9) * 5 : (value * 9) / 5 + 32);
};
const InitialTemperature = (label: string): Temperature => ({
label,
value: 22,
units: "C"
});
interface Actions {
temperature: TemperatureActions<Patch, TemperaturePatch>;
}
interface TemperatureAttrs extends Attrs {
local: TemperatureLocal;
}
const temperature: TemperatureComponent<Patch, TemperaturePatch> = {
Initial: InitialTemperature,
Actions: update => ({
increment: (local, amount) => {
update(local.patch({ value: x => x + amount }));
},
changeUnits: local => {
update(
local.patch(state => {
const value = state.value;
const newUnits = state.units === "C" ? "F" : "C";
const newValue = convert(value, newUnits);
return { ...state, value: newValue, units: newUnits };
})
);
}
})
};
const TemperatureElement: (attrs: TemperatureAttrs) => JSX.Element = ({
state,
local,
actions
}) => (
<div>
{local.get(state).label} Temperature:
{local.get(state).value}&deg;{local.get(state).units}
<div>
<button onClick={() => actions.temperature.increment(local, 1)}>Increment</button>
<button onClick={() => actions.temperature.increment(local, -1)}>Decrement</button>
</div>
<div>
<button onClick={() => actions.temperature.changeUnits(local)}>Change Units</button>
</div>
</div>
);
const app: MergerinoApp<State, Actions> = {
initial: {
temperature: {
air: temperature.Initial("Air"),
water: temperature.Initial("Water")
}
},
Actions: update => ({
temperature: temperature.Actions(update)
})
};
const Root: (attrs: Attrs) => JSX.Element = ({ state, actions }) => (
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
<div>
<TemperatureElement
state={state}
local={nest(["temperature", "air"]) as TemperatureLocal}
actions={actions}
/>
<TemperatureElement
state={state}
local={nest(["temperature", "water"]) as TemperatureLocal}
actions={actions}
/>
</div>
<pre style={{ margin: "0" }}>{JSON.stringify(state, null, 4)}</pre>
</div>
);
const stream = {
stream: (value?: any) => flyd.stream(value),
scan: (acc: any, init: any, stream: any) => flyd.scan(acc, init, stream)
};
const { states, update, actions } = meiosis.mergerino.setup<State, Actions>({
stream,
merge,
app
});
const App = meiosis.react.setup<State, MergerinoPatch<State>, Actions>({ React, Root });
export const reactMergerino = (): void => {
const element = document.getElementById("reactMergerino");
ReactDOM.render(React.createElement(App, { states, update, actions }), element);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment