Skip to content

Instantly share code, notes, and snippets.

@gaearon
Last active March 25, 2024 19:12
Show Gist options
  • Save gaearon/ffd88b0e4f00b22c3159 to your computer and use it in GitHub Desktop.
Save gaearon/ffd88b0e4f00b22c3159 to your computer and use it in GitHub Desktop.
Redux without the sanity checks in a single file. Don't use this, use normal Redux. :-)
function mapValues(obj, fn) {
return Object.keys(obj).reduce((result, key) => {
result[key] = fn(obj[key], key);
return result;
}, {});
}
function pick(obj, fn) {
return Object.keys(obj).reduce((result, key) => {
if (fn(obj[key])) {
result[key] = obj[key];
}
return result;
}, {});
}
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args));
}
export function bindActionCreators(actionCreators, dispatch) {
return typeof actionCreators === 'function' ?
bindActionCreator(actionCreators, dispatch) :
mapValues(actionCreators, actionCreator =>
bindActionCreator(actionCreator, dispatch)
);
}
export function compose(...funcs) {
return arg => funcs.reduceRight((composed, f) => f(composed), arg);
}
export function applyMiddleware(...middlewares) {
return (next) => (reducer, initialState) => {
var store = next(reducer, initialState);
var dispatch = store.dispatch;
var chain = [];
chain = middlewares.map(middleware => middleware({
getState: store.getState,
dispatch: (action) => dispatch(action)
}));
dispatch = compose(...chain)(store.dispatch);
return { ...store, dispatch };
};
}
export function combineReducers(reducers) {
var finalReducers = pick(reducers, (val) => typeof val === 'function');
return (state = {}, action) => mapValues(finalReducers,
(reducer, key) => reducer(state[key], action)
);
}
export function createStore(reducer, initialState) {
var currentReducer = reducer;
var currentState = initialState;
var listeners = [];
var isDispatching = false;
function getState() {
return currentState;
}
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
var index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
function dispatch(action) {
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
listeners.slice().forEach(listener => listener());
return action;
}
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: '@@redux/INIT' });
}
dispatch({ type: '@@redux/INIT' });
return { dispatch, subscribe, getState, replaceReducer };
}
@ryuhangyeong
Copy link

Wow!

@sag1v
Copy link

sag1v commented Mar 28, 2019

4 years later...
I'm curious why you use .slice on this line
listeners.slice().forEach(listener => listener());

Why do we need a new array here?

@raynerpupo
Copy link

raynerpupo commented Apr 10, 2019

@sag1v while that forEach is a blocking operation it doesn't guarantee that a listener() call wouldn't remove a original listener from the listeners array so by copying the list you won't get weird results. 2nd caveat from here: https://redux.js.org/api/store#subscribe.

@raynerpupo
Copy link

@sag1v found the possible reason, please read the 2nd caveat here https://redux.js.org/api/store#subscribe

@sag1v
Copy link

sag1v commented Jun 19, 2020

@raynerpupo For some reason i missed your comments, thanks :)

For other readers, this code snippet can demonstrate what happens if you don't use .slice to create a "snapshot" of the array.
Basically if a listener removes itself from the array, other listeners might not run (due to mutation).

let arr = [func1, func2];
function func1(){
  arr.splice(0, 1) // func1 removes itself from the array
  console.log(1)
}
function func2(){
  console.log(2)
}
arr
//.slice() // if we don't slice here, func2 wont run
.forEach(fn => fn())

// logs: 1

@hacker0limbo
Copy link

2020, starting to learn redux, and got one confusion for this code snippet:

In the createStore function, there should be a third option argument enhancer right? I checked the redux source code and the createStore function might look this:

function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  // ... dispatch, subscribe, getState, replaceReducer, 
}

In this case we can then pass the applyMiddleware to the createStore function:

const store = createStore(
  rootReducer,
  applyMiddleware(
    thunk,
    logger,
  )

Not sure if my thoughts are reasonable, could someone correct me please?

@taaemoh
Copy link

taaemoh commented Oct 7, 2020

I did the same for my Redux Course to explain how really Redux works and what is the concept. I notice that in a lot of courses about Redux, teachers miss the importance of selectors...

Could u please share some link to your course? is it available somewhere?

@ackvf
Copy link

ackvf commented Jan 23, 2024

Poetry

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment