Skip to content

Instantly share code, notes, and snippets.

@sebald
Forked from developit/*state-machine-component.md
Created October 26, 2017 10:07
Show Gist options
  • Save sebald/bc9c0903374128623bc03b51a4230639 to your computer and use it in GitHub Desktop.
Save sebald/bc9c0903374128623bc03b51a4230639 to your computer and use it in GitHub Desktop.
265b lib for building pure functional state machine components. https://npm.im/state-machine-component

state-machine-component

A tiny (265 byte) utility to create state machine components using two pure functions.

🔥 JSFiddle Demo

Usage

The API is a single function that accepts 2 pure functions as arguments:

stateMachineComponent(reduce, render)

The first function, reduce(), takes in the current state and applies an action to it, similar to a reducer in Redux:

// Reduce is a redux-style reducer
function reduce(state, action) {
	// actions are like Redux Standard Actions:
	let { type, data, props } = action

	return { }  // just return the new state
}

The second function, render(), is a pure functional component that gets passed the current state instead of props, and a second argument action() - a function that creates a bound dispatcher for the given action type:

// Render is a functional component with little twist
function render(state, action) {
	// action() creates a dispatcher for an action type:
	return <button onClick={ action('TYPE') } />
}

Simple Example: Counter

// Remember:
//  `state` is the current state.
//  `action` is a redux standard action.
function reduce(state, action) {
	switch (action.type) {
		case '@@INIT': return { count: 0 }
		case 'ADD': return { count: state.count+1 }
	}
}

function render(state, action) {
	return (
		<div class="counter">
			Current count: {state.count}
			<button onClick={action('ADD')}>Add 1</button>
		</div>
	)
}

stateMachineComponent(reduce, render)

Full Example: To-Do List

const ToDos = stateMachineComponent(
	// (state, action)
	({ todos, text }, { type, data, props }) => {
		switch (type) {
			case '@@INIT':return { todos: props.todos || [], text: '' };
			case 'ADD': return { todos: todos.concat(text), text: '' };
			case 'TEXT': return { text: data.target.value };
		}
	},
	// state, action(type)
	({ todos, text }, action) => (
		<div>
			<h2>State Machine ToDos</h2>
			<ul>{todos.map( todo => <li>{todo}</li> )}</ul>
			<form onSubmit={action('ADD')}>
				<input value={text} onInput={action('TEXT')} />
			</form>
		</div>
	)
);
node_modules
npm-debug.log
dist
README.md
import { Component } from 'preact';
export default (reducer, render) => {
function Machine() {
Component.call(this);
let cache = {};
let action = type => cache[type] || (cache[type] = data => {
this.setState(reducer(this.state, { type, data, props: this.props }));
});
this.render = (props, state) => render(state, action);
this.componentWillReceiveProps = action('@@PROPS');
this.componentWillMount = action('@@INIT');
}
return (Machine.prototype = new Component()).constructor = Machine;
};
{
"name": "state-machine-component",
"amdName": "stateMachineComponent",
"version": "1.0.2",
"description": "Create tiny pure state machine components.",
"source": "machine.js",
"module": "dist/machine.es.js",
"main": "dist/machine.js",
"umd:main": "dist/machine.umd.js",
"scripts": {
"build": "npm run transpile && npm run size",
"transpile": "rollup -c --environment FORMAT:umd && rollup -c --environment FORMAT:cjs && rollup -c --environment FORMAT:es",
"size": "strip-json-comments --no-whitespace dist/machine.js | gzip-size",
"test": "eslint machine.js",
"prepublish": "npm run build && cp \"*state-machine-component.md\" README.md"
},
"eslintConfig": {
"extends": "eslint:recommended",
"env": {
"node": true
},
"parserOptions": {
"sourceType": "module"
}
},
"files": [
"machine.js",
"dist"
],
"keywords": [
"preact",
"component",
"state machine",
"redux"
],
"author": "Jason Miller <[email protected]>",
"license": "MIT",
"devDependencies": {
"eslint": "^4.6.1",
"gzip-size": "^3.0.0",
"rollup": "^0.49.2",
"rollup-plugin-buble": "^0.15.0",
"rollup-plugin-post-replace": "^1.0.0",
"rollup-plugin-uglify": "^2.0.1",
"strip-json-comments-cli": "^1.0.1",
"uglify-js": "^2.8.29"
}
}
import fs from 'fs';
import buble from 'rollup-plugin-buble';
import uglify from 'rollup-plugin-uglify';
import replace from 'rollup-plugin-post-replace';
let pkg = JSON.parse(fs.readFileSync('./package.json'));
let format = process.env.FORMAT;
export default {
strict: false,
sourcemap: true,
exports: 'default',
input: pkg.source,
output: {
format,
name: pkg.amdName,
file: format==='es' ? pkg.module : format==='umd' ? pkg['umd:main'] : pkg.main
},
external: ['preact'],
globals: {
preact: 'preact'
},
plugins: [
buble(),
format==='cjs' && replace({
'module.exports = index;': '',
'var index =': 'module.exports ='
}),
format==='umd' && replace({
'return index;': '',
'var index =': 'return'
}),
format!=='es' && uglify({
output: { comments: false },
mangle: {
toplevel: format==='cjs'
}
})
]
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment