Last active
October 2, 2017 21:52
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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