Last active
July 18, 2023 15:31
-
-
Save Jessidhia/afac05ee884c34d588547324ba0d49ec to your computer and use it in GitHub Desktop.
useSelector refactoring
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
function useSelectorWithStoreAndSubscription( | |
selector, | |
equalityFn, | |
store, | |
contextSub | |
) { | |
const subscription = useMemo(() => new Subscription(store, contextSub), [ | |
store, | |
contextSub | |
]) | |
const [lastError, setLastError] = useState() | |
let selectedState, setSelectedState | |
try { | |
const initialSelectedState = useMemo(() => selector(store.getState()), [selector, store, lastError]) | |
// use { value } as wrapper to support equalityFn that are laxer than Object.is | |
void ([{ value: selectedState }, setSelectedState] = useState({ value: initialSelectedState })) | |
if (!Object.is(initialSelectedState, selectedState)) { | |
// can happen from useMemo result changes | |
// ignore equalityFn for this to avoid infinite rerenders on e.g. () => false | |
setSelectedState({ value: initialSelectedState }) | |
selectedState = initialSelectedState | |
} | |
} catch (err) { | |
let errorMessage = `An error occurred while selecting the store state: ${err.message}.` | |
if (lastError) { | |
errorMessage += `\nThe error may be correlated with this previous error:\n${lastError.stack}\n\nOriginal stack trace:` | |
} | |
throw new Error(errorMessage) | |
} | |
const checkForUpdates = useRef() | |
useIsomorphicLayoutEffect(() => { | |
// should this be in the LayoutEffect (i.e. remember only the `selectedState` from the previous commit), | |
// or should this be in the body of the component (i.e. always up to date with the most recent render pass) | |
checkForUpdates.current = () => { | |
try { | |
const newSelectedState = selector(store.getState()) | |
// attempting to do this instead causes missed updates. Why? React bug? | |
// setSelectedState(selectedState => equalityFn(newSelectedState, selectedState) ? selectedState : { value: newSelectedState }) | |
if (equalityFn(newSelectedState, selectedState)) { | |
return | |
} | |
setSelectedState({ value: newSelectedState }) | |
} catch (err) { | |
// we ignore all errors here, since when the component | |
// is re-rendered, the selectors are called again, and | |
// will throw again, if neither props nor store state | |
// changed | |
setLastError(err) | |
} | |
} | |
if (lastError) { | |
setLastError(undefined) | |
} | |
}) | |
useIsomorphicLayoutEffect(() => { | |
subscription.onStateChange = () => checkForUpdates.current() | |
subscription.trySubscribe() | |
checkForUpdates.current() | |
return () => subscription.tryUnsubscribe() | |
}, [store, subscription]) | |
return selectedState | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment