Last active
November 18, 2018 02:45
-
-
Save baetheus/ee94b4cb172eefeafd1ab8c13abcf77e to your computer and use it in GitHub Desktop.
A sample react hook to connect to redux without connect.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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