Created
September 14, 2020 08:16
-
-
Save dmail/a0a6e1b2e10104154a0dc1685bd97dd6 to your computer and use it in GitHub Desktop.
react store stuff
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
export const createSignal = () => { | |
let listeners = [] | |
const listen = (callback) => { | |
let removed = false | |
listeners = [...listeners, callback] | |
return () => { | |
if (removed) return | |
removed = true | |
const listenersWithoutCallback = [] | |
let i = listeners.length | |
let searching = true | |
while (i--) { | |
const listenerCandidate = listeners[i] | |
if (searching) { | |
if (listenerCandidate === callback) { | |
searching = false | |
} else { | |
listenersWithoutCallback.push(listenerCandidate) | |
} | |
} else { | |
listenersWithoutCallback.push(listenerCandidate) | |
} | |
} | |
listeners = listenersWithoutCallback | |
} | |
} | |
const emit = (...args) => { | |
listeners.forEach((listener) => { | |
listener(...args) | |
}) | |
} | |
return { listen, emit } | |
} |
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
/** | |
https://github.com/reactjs/rfcs/pull/119 | |
https://github.com/dai-shi/lets-compare-global-state-with-react-hooks | |
https://github.com/dai-shi/react-hooks-global-state/blob/4ce3e5a43fc0fdb4030718d726f82b17988c1899/src/createContainer.ts#L41 | |
https://github.com/dai-shi/use-context-selector | |
https://github.com/diegohaz/constate | |
https://github.com/facebook/react/tree/master/packages/use-subscription | |
this store sends a getState and dispatch method using context | |
these methods won't rerender the whole tree | |
instead every component calling getState | |
indicate it's interested by the state and gets | |
re-rendered when it changes | |
It means all component not calling useState | |
won't be re-rendered when state changes, a big win | |
But here is the reality: react detect state context usage anyway | |
It mean it's already the case with the naive implementation | |
In other words this implementation provides zero perf boost compare to the naive one | |
Using memoization it's possible to get eventual required perf boost. | |
Moreover https://github.com/reactjs/rfcs/pull/119 is being developed | |
and should provided what we really want: the ability to detect what part of the state | |
are being read so that the element is re-rendered only if the part of the state it reads | |
are modified. | |
Until then we can live with useMemo and wait to see how thing evolves | |
*/ | |
import React from "react" | |
import { createSignal } from "./createSignal.js" | |
const { createContext, useContext, useReducer, useEffect, useRef } = React | |
export const createStateStore = (defaultState, { init = (state) => state, effect } = {}) => { | |
const GetStateContext = createContext(null) | |
const DispatchContext = createContext(null) | |
let stateValue | |
const stateSignal = createSignal() | |
const reducer = (state, action) => action(state) | |
const getState = () => { | |
const [value, setValue] = React.useState(stateValue) | |
useEffect(() => { | |
return stateSignal.listen(setValue) | |
}) | |
return value | |
} | |
const ContextProvider = ({ initialState, children }) => { | |
const [state, dispatch] = useReducer(reducer, defaultState, () => init(defaultState)) | |
stateValue = state | |
const previousStateRef = useRef(state) | |
useEffect(() => { | |
if (effect) effect(state) | |
if (previousStateRef.current !== state) { | |
previousStateRef.current = state | |
stateSignal.emit(state) | |
} | |
}, [state]) | |
useEffect(() => { | |
if (!initialState) return | |
dispatch((state) => { | |
return { ...state, ...initialState } | |
}) | |
}, [initialState]) | |
return ( | |
<GetStateContext.Provider value={getState}> | |
<DispatchContext.Provider value={dispatch}>{children}</DispatchContext.Provider> | |
</GetStateContext.Provider> | |
) | |
} | |
const useState = () => useContext(GetStateContext)() | |
const useDispatch = () => useContext(DispatchContext) | |
const createAction = (actionReducer) => createStoreAction({ useDispatch }, actionReducer) | |
return { | |
ContextProvider, | |
useState, | |
useDispatch, | |
createAction, | |
} | |
} | |
export const createStoreAction = (store, actionReducer) => { | |
return (dispatch = store.useDispatch()) => { | |
return (...args) => { | |
dispatch((state) => actionReducer(state, ...args)) | |
} | |
} | |
} |
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 from "react" | |
const { createContext, useContext, useReducer, useEffect } = React | |
export const createStateStoreNaive = (defaultState, { init = (state) => state, effect } = {}) => { | |
const StateContext = createContext(null) | |
const DispatchContext = createContext(null) | |
const reducer = (state, action) => action(state) | |
const ContextProvider = ({ initialState, children }) => { | |
const [state, dispatch] = useReducer(reducer, defaultState, () => init(defaultState)) | |
useEffect(() => { | |
if (effect) effect(state) | |
}, [state]) | |
useEffect(() => { | |
if (!initialState) return | |
dispatch((state) => { | |
return { ...state, ...initialState } | |
}) | |
}, [initialState]) | |
return ( | |
<DispatchContext.Provider value={dispatch}> | |
<StateContext.Provider value={state}>{children}</StateContext.Provider> | |
</DispatchContext.Provider> | |
) | |
} | |
const useState = () => useContext(StateContext) | |
const useDispatch = () => useContext(DispatchContext) | |
const createAction = (actionReducer) => createStoreAction({ useDispatch }, actionReducer) | |
return { | |
ContextProvider, | |
useState, | |
useDispatch, | |
createAction, | |
} | |
} | |
export const createStoreAction = (store, actionReducer) => { | |
return (dispatch = store.useDispatch()) => { | |
return (...args) => { | |
dispatch((state) => actionReducer(state, ...args)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment