Skip to content

Instantly share code, notes, and snippets.

@tunnckoCore
Last active October 2, 2017 21:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tunnckoCore/e0a31307059baec279593602e87809a3 to your computer and use it in GitHub Desktop.
Save tunnckoCore/e0a31307059baec279593602e87809a3 to your computer and use it in GitHub Desktop.
Fractal & functional state management for unidirectional data flow, a la Elm in just 2kb
// SSR with just registering for ESM:
// import 'mindom/import' // 1.1kb
// or register for CJS:
// require('mindom/require') // 1.1kb
import {
morph,
createView,
createStore,
createEffects,
createReducers,
createElement, // eslint-disable-line no-unused-vars
} from './src/index.js' // 2.2kb
/**
* STORE
*/
const state = {
todos: [],
complete: 0,
}
const STORE = createStore(state)
// always set error listener,
// everything comes here!
// - handles errored effect
// - handles errored reducer
// - handles errored view
// - handles called `done(err)` from view
STORE.on('error', (er) => {
console.log('ERR!', er.message)
})
STORE.on('render', (view, ctx, handler) =>
handler((oldEl) => morph(oldEl, view(ctx)))
)
/**
* REDUCERS
*/
const pureReducers = {
addTodo: ({ state }, text) => ({
todos: state.todos.concat({
id: state.todos.length + 1,
done: false,
text,
}),
}),
delTodo: ({ state }, id) => ({
todos: state.todos.filter((todo) => todo.id !== id),
}),
doneTodo: ({ state }, id) => ({
todos: state.todos.map(
(todo) => (todo.id === id ? Object.assign(todo, { done: true }) : todo)
),
complete: state.complete + 1,
}),
}
const REDUCERS = createReducers(STORE, pureReducers)
/**
* EFFECTS
*/
const FAKEDATA = [
{ title: 'foo bar' },
{ title: 'get coffee' },
{ title: 'super awesome' },
]
const someEffects = {
fetchData: ({ state, actions }) => Promise.resolve(FAKEDATA),
feed: ({ state, actions }) =>
actions
.fetchData()
.then((res) => res.map((r) => ({ text: r.title })))
.then((res) => {
res.forEach(({ text }) => {
actions.addTodo(text)
})
}),
list: ({ state }) => {
state.todos.forEach((todo) => {
console.log('name:', todo.text, todo.done ? 'done' : '')
})
console.log('completed:', state.complete)
},
}
// "effects" variable can be called "actions" too and vice versa,
// because basically it includes both the reducers and the effects
// passing "reducers" as last argument is optional
const ACTIONS = createEffects(STORE, someEffects, REDUCERS)
/**
* FUNCTIONAL COMPONENT
*/
const Todo = ({ state, actions, done }) => {
if (state.todos.length) {
return (
<ul>
{state.todos.map((todo) => (
<li>
{todo.id}) {todo.text} / completed: {todo.done}
</li>
))}
</ul>
)
}
return <div>no todos</div>
}
/**
* MAIN VIEW
*/
const VIEW = ({ state, actions, done }) => {
const onenter = (e) => {
// on hit enter/return only
if (e.keyCode === 13) {
actions.addTodo(e.target.value)
e.target.value = ''
}
}
return (
<div>
<h1>Hello World! Todos: {state.todos.length}</h1>
<Todo state={state} actions={actions} done={done} />
<input type="text" placeholder="my todo here..." onKeyDown={onenter} />
</div>
)
}
const element = createView(STORE, ACTIONS, VIEW)
/**
* INITIAL MOUNT
*/
document.body.insertBefore(element, document.body.firstChild)
// SSR with just and nothing more
// renderToString(document.body)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment