Skip to content

Instantly share code, notes, and snippets.

@finalfantasia
Last active November 10, 2016 06:56
Show Gist options
  • Save finalfantasia/b3c9f29387f0634288fdb6d5d6eb57e7 to your computer and use it in GitHub Desktop.
Save finalfantasia/b3c9f29387f0634288fdb6d5d6eb57e7 to your computer and use it in GitHub Desktop.
React-Redux Todo App
// <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
// <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>
/*
<body>
<div id="root" />
</body>
*/
// redux api
const combineReducers = reducers =>
(state = {}, action) =>
Object.keys (reducers).
reduce (
(accumulator, key) =>
Object.assign ({}, accumulator, {[key]: reducers [key] (state [key], action)}),
{})
const createStore = reducer => {
let state
let listeners = []
const getState = () => state
const subscribe = listener => {
listeners.push (listener)
const unsubscribe = () => listeners = listeners.filter (l => l !== listener)
return unsubscribe
}
const dispatch = action => {
state = reducer (state, action)
listeners.forEach (listener => listener ())
}
dispatch ({})
return {
getState,
subscribe,
dispatch
}
}
// redux reducers
const todo = (state = {}, {type, id, text}) => {
switch (type) {
case 'ADD_TODO': return {
id: id,
text: text,
done: false
}
case 'TOGGLE_TODO':
return Object.assign ({}, state, {done: !state.done})
default: return state
}
}
const todos = (state = [], action) => {
const {type, id, text} = action
switch (type) {
case 'ADD_TODO': return [
...state,
todo ({}, action)
]
case 'TOGGLE_TODO':
return state.map (t => t.id === id ? todo (t, {type: type}) : t)
default: return state
}
}
const visibility = (state = '', {type, visibility}) => {
switch (type) {
case 'SET_VISIBILITY': return visibility
default: return state
}
}
const nextId = (state = 0, {type}) => {
switch (type) {
case 'INCREMENT_ID': return state + 1
default: return state
}
}
const todoApp = combineReducers ({todos, nextId, visibility})
// react components
const Filter = ({store, visibility, children}) => (
<button disabled={store.getState ().visibility === visibility}
onClick={event => {
store.dispatch ({
type: 'SET_VISIBILITY',
visibility
})
}}>
{children}
</button>
)
const TodoApp = ({store}) => {
const todoFilter = visibility => todo => {
switch (visibility) {
case 'DONE_ONLY': return todo.done
case 'NOT_DONE_ONLY': return !todo.done
case 'ALL': return true
default: return true
}
}
let input
return (
<div>
<input ref={node => input = node} />
<button onClick={() => {
store.dispatch ({type: 'ADD_TODO', text: input.value, id: store.getState ().nextId})
store.dispatch ({type: 'INCREMENT_ID'})
input.value = ''}}>
Add
</button>
<p>
Show:
<Filter store={store} visibility='ALL'>All</Filter>
<Filter store={store} visibility='DONE_ONLY'>Done</Filter>
<Filter store={store} visibility='NOT_DONE_ONLY'>Not Done</Filter>
</p>
<ul>
{store.getState ().todos.filter (todoFilter (store.getState ().visibility)).map (todo =>
<li key={todo.id} onClick={() => store.dispatch ({type: 'TOGGLE_TODO', id: todo.id})}>
{todo.done ? '🏁' : '🏳️'} {todo.text}
</li>
)}
</ul>
</div>
)
}
// app
const store = createStore (todoApp)
const render = () => ReactDOM.render (
<TodoApp store={store}/>,
document.querySelector ('#root')
)
store.subscribe (render)
render ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment