262588213843476 · Gist

Redux without redux - sharing state and one way data flow using only standard react

// More complex example implementing the classic "todo" application
import React from 'react';
import ReactDOM from 'react-dom';
import {StateDispatcher} from './StateDispatcher';
// Utility function for creating combined reducers. Not necessary, but nice to have
const combineReducers = (reducers) => (state, action) => {
return Object.entries(reducers).reduce((newState, [reducerKey, reducer]) => {
return {
...newState,
[reducerKey]: reducer(state[reducerKey], action)
}
}, state);
};
const initialState = {
todos: [
{
title: 'Do something',
id: -1,
complete: false
}
],
filter: 'all',
newTodoText: ''
};
// Used to generate new ids for the todos
let nextId = 0;
/*
Reducers
*/
const todosReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO': {
return [
...state,
{
title: action.payload,
id: nextId++,
complete: false
}
];
}
case 'TOGGLE_TODO':{
const findTodo = todo => todo.id === action.payload;
const todo = state.find(findTodo);
return [
...state.filter(todo => !findTodo(todo)),
{
...todo,
complete: !todo.complete
}
];
}
default: return state;
}
}
const filterReducer = (state, action) => {
switch (action.type) {
case 'APPLY_FILTER': return action.payload
default: return state;
}
}
const newTodoTextReducer = (state, action) => {
switch (action.type) {
case 'SET_NEW_TODO_TEXT': return action.payload;
case 'ADD_TODO': return '';
default: return state;
}
};
// The final combined reducer
const rootReducer = combineReducers({
todos: todosReducer,
filter: filterReducer,
newTodoText: newTodoTextReducer
});
const applyFilter = (filter, todos) => todos.filter(todo => {
switch (filter) {
case 'completed':
return todo.complete;
case 'uncompleted':
return !todo.complete;
default:
return true;
}
});
ReactDOM.render(
<StateDispatcher state={initialState} reducer={rootReducer}>
{({state, dispatch}) => {
return <div>
<div>
<ul>
{applyFilter(state.filter, state.todos).map(todo =>
<li
key={todo.id}
onClick={() => dispatch({type: 'TOGGLE_TODO', payload: todo.id})}
style={{
textDecoration: todo.complete ? 'line-through' : 'none'
}}
>
{todo.title}
</li>
)}
</ul>
</div>
<div>
<button onClick={() => dispatch({type: 'APPLY_FILTER', payload: 'all'})}>All</button>
<button onClick={() => dispatch({type: 'APPLY_FILTER', payload: 'completed'})}>Completed</button>
<button onClick={() => dispatch({type: 'APPLY_FILTER', payload: 'uncompleted'})}>Uncompleted</button>
</div>
<div>
<input
value={state.newTodoText}
onChange={e => dispatch({type: 'SET_NEW_TODO_TEXT', payload: e.target.value })}
onKeyPress={e =>
(e.key === 'Enter' && state.newTodoText)
? dispatch({type: 'ADD_TODO', payload: state.newTodoText})
: null
}
/>
<button
onClick={() => state.newTodoText && dispatch({type: 'ADD_TODO', payload: state.newTodoText})}
>
Add Todo
</button>
</div>
</div>
}}
</StateDispatcher>,
document.getElementById('root')
);

Read the original on gist.github.com ↗