Skip to content

Instantly share code, notes, and snippets.

@chapel
Last active February 21, 2016 05:44
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 chapel/bff6870a82009a6a4ad0 to your computer and use it in GitHub Desktop.
Save chapel/bff6870a82009a6a4ad0 to your computer and use it in GitHub Desktop.
playing around with redux
import { createAction, createEffect, reduceActions } from './playground';
export const addTodo = createAction('ADD_TODO', (state, text) => {
return [
{
id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
completed: false,
text
},
...state
]
});
export const deleteTodo = createAction('DELETE_TODO', (state, id) => {
return state.filter(todo =>
todo.id !== id
)
});
export const editTodo = createAction('EDIT_TODO', (state, id, text) => {
return state.map(todo =>
todo.id === id ?
{ ...todo, text } :
todo
);
});
export const completeTodo = createAction('COMPLETE_TODO', (state, id) => {
return state.map(todo =>
todo.id === id ?
{ ...todo, completed: !todo.completed } :
todo
);
});
export const completeAll_ = createAction('COMPLETE_ALL', (state) => {
const areAllMarked = state.every(todo => todo.completed)
return state.map(todo => ({
...todo,
completed: !areAllMarked
}));
});
const asyncAddTodo = createEffect('ASYNC_ADD_TODO', (dispatch, getState, text) => {
setTimeout(() => {
dispatch(addTodo(text));
}, 1000);
});
export const completeAll = createEffect('COMPLETE_ALL_EFFECT', (dispatch) => {
dispatch(completeAll_());
dispatch(asyncAddTodo('Foo Bar'));
});
export const clearCompleted = createAction('CLEAR_COMPLETED', (state) => {
return state.filter(todo => todo.completed === false);
});
const initialState = [
{
text: 'Use Redux',
completed: false,
id: 0
}
]
export const reducer = reduceActions([
addTodo,
deleteTodo,
editTodo,
completeTodo,
completeAll_,
clearCompleted
], initialState);
import { createStore, applyMiddleware } from 'redux'
import rootReducer from '../reducers'
import { effectMiddleware } from './playground';
export default function configureStore(initialState) {
const store = createStore(rootReducer, applyMiddleware(effectMiddleware), initialState)
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers').default
store.replaceReducer(nextReducer)
})
}
return store
}
import { createAction, createEffect, reduceActions } from './playground';
export const simpleAction = createAction('SIMPLE_ACTION', (state) => {
return {
...state,
simple: true
};
});
export const complexAction = createAction('COMPLEX_ACTION');
complexAction.update((state, arg) => {
if (arg > 100) {
return {
..state,
argOver100: true
};
}
return state;
});
complexAction.update((state, arg) => {
if (arg > 1000) {
return {
..state,
argOver1000: true
};
}
return state;
});
complexAction.update((state, arg) => {
if (arg === 599) {
return {
..state,
price: arg
};
}
return state;
});
export const reducer = reduceActions([
simpleAction,
complexAction
], initialState);
export const isAction = Symbol();
export const isEffect = Symbol();
export const createAction = (name = '', ...reducers) => {
const type = Symbol(name);
const actionReducers = new Set(reducers);
const action = (...args) => {
return {
type,
args
};
};
Object.assign(action, {
_name: name,
type: type,
isAction,
update: (...reducers) => {
for (const reducer of reducers) {
actionReducers.add(reducer);
}
return this;
},
reduce: (state, { args }) => {
let newState = state;
for (const reducer of actionReducers) {
let reducedState = reducer(state, ...args);
if (reducedState) {
newState = reducedState;
}
}
return newState;
}
});
return action;
};
export const createEffect = (name = '', effect) => {
const baseAction = createAction(name);
const action = (...args) => {
return {
...baseAction(...args),
isEffect,
effect
};
};
action._name = name;
action.type = baseAction.type;
return action;
};
export const effectMiddleware = ({ dispatch, getState }) => {
return (next) => ({ effect, ...action }) => {
action.isEffect === isEffect ?
effect(dispatch, getState, ...action.args) :
next(action);
};
};
export const reduceActions = (rawActions, initialState) => {
if (!Array.isArray(rawActions)) {
rawActions = Object.values(rawActions);
}
const actionMap = new Map(rawActions.filter((action) => {
return action && action.isAction === isAction;
}).map((action) => {
return [action.type, action];
}));
return (state = initialState, action) => {
let newState = state;
if (actionMap.has(action.type)) {
const handler = actionMap.get(action.type);
newState = handler.reduce(state, action);
}
return newState;
};
};
import { combineReducers } from 'redux'
import { reducer } from '../actions';
const rootReducer = combineReducers({
todos: reducer
})
export default rootReducer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment