Skip to content

Instantly share code, notes, and snippets.

@haishanh
Created March 14, 2020 04:07
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 haishanh/f9ac060ac5fdc6fb734f4aebe76c2a1a to your computer and use it in GitHub Desktop.
Save haishanh/f9ac060ac5fdc6fb734f4aebe76c2a1a to your computer and use it in GitHub Desktop.
redux-with-hooks
import { createStore, applyMiddleware, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../ducks';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
function configureStore() {
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(thunkMiddleware))
);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../ducks', () => {
const nextRootReducer = require('../ducks').default;
store.replaceReducer(nextRootReducer);
});
}
// eslint-disable-next-line no-undef
if (__DEV__) {
window.getState = store.getState;
}
return store;
}
const store = configureStore();
export { store };
const hasOwn = Object.prototype.hasOwnProperty;
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// eslint-disable-next-line no-self-compare
return x !== x && y !== y;
}
}
export default function shallowEqual(objA, objB) {
if (is(objA, objB)) return true;
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
import React, {
createContext,
useState,
useEffect,
useContext,
useMemo
} from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import shallowEqual from './shallowEqual';
const StoreContext = createContext(null);
export function Provider({ store, children }) {
return (
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
}
Provider.propTypes = {
store: PropTypes.object,
children: PropTypes.node
};
export function useStore() {
// return the context
// which is the redux store
return useContext(StoreContext);
}
function bindActions(actions, dispatch) {
const a = typeof actions === 'function' ? actions() : actions;
return bindActionCreators(a, dispatch);
}
export function useActions(actions) {
const { dispatch } = useStore();
return useMemo(() => bindActions(actions, dispatch), [actions, dispatch]);
}
export function useStoreState(selector) {
const store = useStore();
const initialMappedState = selector(store.getState());
const [compState, setCompState] = useState(initialMappedState);
// subscribe to store change
useEffect(() => {
let compStateCurr = compState;
return store.subscribe(() => {
const compStateNext = selector(store.getState());
if (shallowEqual(compStateCurr, compStateNext)) return;
// update state if not equal
compStateCurr = compStateNext;
setCompState(compStateNext);
});
}, [compState, selector, store]);
return compState;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment