Skip to content

Instantly share code, notes, and snippets.

@baetheus
Last active November 18, 2018 02:45
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 baetheus/ee94b4cb172eefeafd1ab8c13abcf77e to your computer and use it in GitHub Desktop.
Save baetheus/ee94b4cb172eefeafd1ab8c13abcf77e to your computer and use it in GitHub Desktop.
A sample react hook to connect to redux without connect.
import { useContext, useEffect, Context, useState } from 'react';
import { Store } from 'redux';
/**
* Creates a useRedux hook.
*
* First function takes Context.
*
* Second function takes a selector and an optional comparator and
* returns the output of the selector and the store's dispatch function
*
* Updates only when comparator detects a change (by default on strict equality change)
*/
export const useReduxFactory = <S>(context: Context<Store<S>>) => <O>(
selector: (s: S) => O,
comparator: (p: O, n: O) => boolean = (p, n) => p !== n
) => {
const { dispatch, subscribe, getState } = useContext(context);
const [state, setState] = useState<O>(selector(getState()));
const listener = () => {
const nextState = selector(getState());
if (comparator(state, nextState)) {
setState(nextState);
}
};
useEffect(() => subscribe(listener), [context, selector, comparator]);
return [state, dispatch] as [typeof state, typeof dispatch];
};
import * as React from 'react';
import { render } from 'react-dom';
import { store, StoreContext, useRedux, changeCount } from './store';
const Counter: React.SFC = () => {
const [count, dispatch] = useRedux(s => s.count);
const inc = () => dispatch(changeCount(c => c + 1));
const dec = () => dispatch(changeCount(c => c - 1));
return (
<section className="ba-7 ba-sm-2 ba-solid">
<h1>Count : {count}</h1>
<button onClick={inc}>Increment</button>
<button onClick={dec}>Decrement</button>
</section>
);
};
const App: React.SFC = () => (
<StoreContext.Provider value={store}>
<Counter />
</StoreContext.Provider>
);
const container = document.getElementById('app');
render(<App />, container);
import { createContext } from 'react';
import { combineReducers, createStore } from 'redux';
import { useReduxFactory } from './useReduxFactory';
// Boilerplate Reducer, Types, and Store
const interface Action<T extends string = string> {
type: T;
}
interface CountStore {
count: number;
}
const INITIAL_COUNT_STORE: CountStore = {
count: 0;
}
const countReducer = (s: S, a: Action) => {
switch (a.type) {
case 'CHANGE_COUNT': return { ...s, count: a.payload(s.count) };
default: return s;
}
const changeCount = (payload: (s: number) => number) => ({ type: 'CHANGE_COUNT', payload });
// Create Store Factory
const configureStore = () => {
const rootReducer = combineReducers({
count: countReducer
});
const store = createStore(rootReducer);
const StoreContext = createContext(store);
return {
store,
StoreContext
};
};
// Create/Export Store and useRedux Hook
export const { store, StoreContext } = configureStore();
export const useRedux = useReduxFactory(StoreContext);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment