Last active
November 19, 2018 18:51
-
-
Save goloveychuk/7ccc26fac1ee99333bdf0451a098bb1f to your computer and use it in GitHub Desktop.
redux-hooks.tsx
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 React, { | |
Component, | |
useContext, | |
useMemo, | |
useState, | |
useEffect | |
} from "react"; | |
import { ReactReduxContext, Provider } from "react-redux"; | |
import { Selector } from "reselect"; | |
import { | |
combineReducers, | |
AnyAction, | |
Dispatch, | |
ActionCreator, | |
Store | |
} from "redux"; | |
import { createStore } from "redux"; | |
const MyStoreContext = React.createContext<Store | null>(null); | |
export function bindDispatch<T extends ActionCreator<any>>( | |
fn: T, | |
dispatch: Dispatch | |
): T { | |
return (((...args: any[]) => dispatch((fn as any)(...args))) as any) as T; | |
} | |
function createRedux(context: React.Context<Store | null> = MyStoreContext) { | |
function useSelector<S>(selector: Selector<AppState, S>) { | |
const store = useContext(context)!; | |
const [compState, setCompState] = useState(() => | |
selector(store.getState()) | |
); | |
let prevState = compState; | |
useEffect(() => { | |
console.log("calling subscribe"); | |
return store.subscribe(() => { | |
const newState = selector(store.getState()); | |
if (prevState !== newState) { | |
// debugger; | |
// console.log('setting new state') | |
prevState = newState; | |
setCompState(newState); | |
} | |
}); | |
}, []); | |
return compState; | |
} | |
function useAction<T extends ActionCreator<any>>(action: T): T { | |
const store = useContext(context)!; | |
// const [compState, setCompState] = useState(()=>selector(store.getState())); | |
return bindDispatch(action, store.dispatch); | |
} | |
const ReduxProvider: React.SFC<{ store: Store }> = ({ store, children }) => ( | |
<context.Provider value={store}> | |
<Provider store={store}>{children}</Provider> | |
</context.Provider> | |
); | |
return { useAction, useSelector, ReduxProvider }; | |
} | |
const { useAction, useSelector, ReduxProvider } = createRedux(); | |
//////////////////////////////////////////////////////////////////////////////////// | |
interface ListState { | |
list: {}[]; | |
} | |
interface CountState { | |
count: number; | |
} | |
interface AppState { | |
list: ListState; | |
count: CountState; | |
} | |
function countSelector(state: AppState) { | |
return state.count.count; | |
} | |
function listSelector(state: AppState) { | |
return state.list.list; | |
} | |
function incrementAction() { | |
return { | |
type: "INCREMENT" | |
}; | |
} | |
function updateList() { | |
return { | |
type: "UPDATE_LIST" | |
}; | |
} | |
function countReducer( | |
state: CountState = { count: 13 }, | |
action: AnyAction | |
): CountState { | |
switch (action.type) { | |
case "INCREMENT": | |
return { | |
...state, | |
count: state.count + 1 | |
}; | |
default: | |
return state; | |
} | |
} | |
function listReducer(state: ListState = { list: [] }, action: any): ListState { | |
switch (action.type) { | |
case "UPDATE_LIST": | |
return { | |
...state, | |
list: [...state.list, {}] | |
}; | |
default: | |
return state; | |
} | |
} | |
const rootReducer = combineReducers({ | |
list: listReducer, | |
count: countReducer | |
}); | |
//////////////////////////////////////////////////////////////////////////////////////// | |
const Button = () => { | |
const num = useSelector(countSelector); | |
const onClick = useAction(incrementAction); | |
console.log("rerendering button"); | |
return ( | |
<div> | |
<button onClick={onClick}>{`button ${num}`}</button> | |
</div> | |
); | |
}; | |
const List = () => { | |
const list = useSelector(listSelector); | |
const onClick = useAction(updateList); | |
console.log("rerendering list"); | |
return ( | |
<div> | |
<button onClick={onClick}>{`update list`}</button> | |
{list.map((_, ind) => ( | |
<div key={ind}>listItem</div> | |
))} | |
</div> | |
); | |
}; | |
function StatelessList() { | |
console.log("rerendering stateless list!!!!!!!, WHY?"); | |
return null; | |
} | |
class App extends Component { | |
store = createStore(rootReducer); | |
render() { | |
return ( | |
<ReduxProvider store={this.store}> | |
<div className="App"> | |
<Button /> | |
<List /> | |
<StatelessList /> | |
</div> | |
</ReduxProvider> | |
); | |
} | |
} | |
export default App; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment