Skip to content

Instantly share code, notes, and snippets.

@likwrk
Last active December 1, 2018 23:31
Show Gist options
  • Save likwrk/41be6d28d64228b99a201ec70bdff405 to your computer and use it in GitHub Desktop.
Save likwrk/41be6d28d64228b99a201ec70bdff405 to your computer and use it in GitHub Desktop.
simple Flux implementation inspired by redux refactored with combine reducers and select
function combineReducers(reducers) {
const initState = {};
const finalReducers = Object.keys(reducers).map((key) => {
const reducer = reducers[key];
initState[key] = reducers[key](undefined, { type: null });
return { key, reducer };
});
return (state = initState, action) => {
const newState = {};
let changed = false;
let i = finalReducers.length;
while (i--) {
const { key, reducer } = finalReducers[i];
const prevState = state[key];
const nextState = reducer(prevState, action);
if (!changed && prevState !== nextState) changed = true;
newState[key] = nextState;
}
return changed ? newState : state;
};
}
function Store(reducer) {
let state = reducer(undefined, { type: null });
const subscribers = [];
function notifySubscribers(prevState) {
let i = subscribers.length;
while (i--) {
const { callback, select } = subscribers[i];
const value = select ? select(state) : state;
const prevValue = select ? select(prevState) : prevState;
if (prevValue !== value) callback(value);
}
}
function reduce(action) {
if (!action || !action.type) return;
const prevState = state;
state = reducer(prevState, action);
if (state === prevState) return;
notifySubscribers(prevState);
}
this.getState = () => state;
this.dispatch = (action) => {
if (typeof action === 'function') {
action(this.dispatch, this.getState);
} else {
reduce(action);
}
};
this.subscribe = (callback, select = null) => {
subscribers.push({ callback, select });
return () => {
let i = subscribers.length;
while (i--) {
if (subscribers[i].callback !== callback) continue;
subscribers.splice(i, 1);
break;
}
};
};
}
/* class Store {
constructor(reducer) {
this.reducer = reducer;
this.state = this.reducer(undefined, { type: null });
this.subscribers = [];
this.dispatch = this.dispatch.bind(this);
this.getState = this.getState.bind(this);
}
subscribe(callback, select = null) {
this.subscribers.push({ callback, select });
return () => {
let i = this.subscribers.length;
while (i--) {
if (this.subscribers[i].callback !== callback) continue;
this.subscribers.splice(i, 1);
break;
}
};
}
reduce(action) {
if (!action || !action.type) return;
const prevState = this.state;
this.state = this.reducer(prevState, action);
if (this.state === prevState) return;
this.notifySubscribers(this.state, prevState);
}
getState() {
return this.state;
}
notifySubscribers(state, prevState) {
let i = this.subscribers.length;
while (i--) {
const { callback, select } = this.subscribers[i];
const value = select ? select(state) : state;
const prevValue = select ? select(prevState) : prevState;
if (prevValue !== value) callback(value);
}
}
dispatch(action) {
if (typeof action === 'function') {
action(this.dispatch, this.getState, this.state);
} else {
this.reduce(action);
}
}
} */
const counter = (state = 0, action) => {
if (action.type !== 'inc') return state;
return state + action.value;
};
const increment1 = () => ({ type: 'inc', value: 1 });
const increment = value => (dispatch, getState) => {
setTimeout(() => {
dispatch({ type: 'inc', value });
}, 500);
};
const store = new Store(combineReducers({ counter }));
console.log(store);
store.subscribe((state) => {
console.log('subscribe', state);
});
const unsubscribe = store.subscribe((state) => {
console.log('subscribe with key', state);
}, state => state.counter);
store.dispatch(increment(2));
store.dispatch(increment1());
unsubscribe();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment