Skip to content

Instantly share code, notes, and snippets.

@softwarespot
Created November 6, 2017 09:42
Show Gist options
  • Save softwarespot/a046556a13e5d37e5a69ba1e84d0d90c to your computer and use it in GitHub Desktop.
Save softwarespot/a046556a13e5d37e5a69ba1e84d0d90c to your computer and use it in GitHub Desktop.
Basic redux implementation
/* eslint-disable */
var app = {
utils: {
isArray() {
return Array.isArray
},
each(arr, fn) {
return arr.forEach(fn);
},
filter(arr, fn) {
return arr.filter(fn);
},
keys(obj) {
return Object.keys(obj);
},
reduce(obj, fn, inital) {
if (Array.isArray(obj)) {
return obj.reduce(fn, inital);
}
return app.utils.keys(obj)
.reduce((current, key) => fn(current, obj[key], key), inital);
},
isEmpty(obj) {
return app.utils.keys(obj).length === 0;
}
}
};
app.redux = (function () {
// createStore(reducer, [preloadedState], [enhancer])
// combineReducers(reducers)
// Not done
// applyMiddleware(...middlewares)
// bindActionCreators(actionCreators, dispatch)
// compose(...functions)
return {
createStore: createStore,
combineReducers: combineReducers
};
// Store
// getState()
// dispatch(action)
// subscribe(listener)
// replaceReducer(nextReducer)
function createStore(reducer, preloadedState /* enhancer */) {
_validateByType(reducer, 'function', 'reducer');
var state = preloadedState;
var subscribers = [];
dispatch({
type: 'app.redux:init'
});
return {
getState: getState,
dispatch: dispatch,
subscribe: subscribe,
replaceReducer: replaceReducer
};
function getState() {
return state;
}
function dispatch(action) {
_validateByType(action, 'object', 'action');
if (!action.hasOwnProperty('type')) {
throw new Error('Invalid action object, missing a property of "type"');
}
state = reducer(state, action);
app.utils.each(subscribers, function (fn) {
fn();
});
return action;
}
function subscribe(fn) {
_validateByType(fn, 'function', 'fn');
subscribers = subscribers.concat(fn);
return function () {
if (fn) {
// Remove all with the same function reference, slightly different than Rredux which removes
// the first function reference found
subscribers = app.utils.filter(subscribers, function (subscriber) {
return fn !== subscriber;
});
fn = undefined;
}
};
}
function replaceReducer(newReducer) {
_validateByType(newReducer, 'function', 'newReducer');
reducer = newReducer;
}
}
function combineReducers(reducers) {
_validateByType(reducers, 'object', 'reducers');
if (app.utils.isEmpty(reducers)) {
throw new Error('Invalid "reducers" argument, no reducers defined');
}
return function (state, action) {
return app.utils.reduce(reducers, function (nextState, fn, key) {
nextState[key] = fn(nextState[key], action);
return nextState;
}, state || {});
};
}
function _validateByType(value, type, name) {
if (typeof value !== type) {
throw new Error('Invalid data type for the "' + name + '" argument, expected a function');
}
}
}());
// Example
const state = {
todos: [],
count: 0,
};
const reducers = app.redux.combineReducers({
todos: todoReducer
});
// const store = app.redux.createStore(reducers);
const store = app.redux.createStore((state = {x:7}, action) => {
return Object.assign({}, state, {
count: state.count + 1
});
});
const unsubscribe = store.subscribe(() => {
console.log('1. Store was changed', store.getState());
});
unsubscribe();
store.subscribe(() => {
console.log('2. Store was changed', store.getState());
});
// Dispatch events
store.dispatch({
type: 'ADD_TODO',
todo: {
id: 1,
text: 'Example 1',
},
});
store.dispatch({
type: 'ADD_TODO',
todo: {
id: 2,
text: 'Example 2',
},
});
console.log(store.getState());
store.dispatch({
type: 'REMOVE_TODO',
id: 2,
});
console.log(store.getState());
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
action.todo
];
case 'REMOVE_TODO':
return state.filter((todo) => action.id !== todo.id);
}
return state;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment