Skip to content

Commit

Permalink
combine the idea used in reduxjs#1505
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi committed Jan 27, 2020
1 parent 76a1d08 commit 32481c9
Showing 1 changed file with 28 additions and 56 deletions.
84 changes: 28 additions & 56 deletions src/hooks/useSelector.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo, useContext } from 'react'
import { useReducer, useEffect, useMemo, useContext } from 'react'
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
import Subscription from '../utils/Subscription'
import { ReactReduxContext } from '../components/Context'
Expand All @@ -11,72 +11,44 @@ function useSelectorWithStoreAndSubscription(
store,
contextSub
) {
const [state, setState] = useState(() => ({
storeState: store.getState(),
selector,
selectedState: selector(store.getState())
// subscriptionCallbackError: undefined
}))
const [state, dispatch] = useReducer(
(prevState, storeState) => {
const nextSelectedState = selector(storeState)
if (equalityFn(nextSelectedState, prevState.selectedState)) {
// bail out
return prevState
}
return {
selector,
storeState,
selectedState: nextSelectedState
}
},
store.getState(),
storeState => ({
selector,
storeState,
selectedState: selector(storeState)
})
)

const subscription = useMemo(() => new Subscription(store, contextSub), [
store,
contextSub
])

let selectedState = state.selectedState

try {
if (selector !== state.selector || state.subscriptionCallbackError) {
const newSelectedState = selector(state.storeState)
if (!equalityFn(newSelectedState, selectedState)) {
selectedState = newSelectedState
// schedule another update
setState(prevState => ({
...prevState,
selector,
selectedState
// subscriptionCallbackError: undefined
}))
}
}
} catch (err) {
if (state.subscriptionCallbackError) {
err.message += `\nThe error may be correlated with this previous error:\n${state.subscriptionCallbackError.stack}\n\n`
if (state.selector !== selector) {
const nextSelectedState = selector(state.storeState)
if (!equalityFn(nextSelectedState, state.selectedState)) {
selectedState = nextSelectedState
// schedule another update
dispatch(state.storeState)
}

throw err
}

useEffect(() => {
function checkForUpdates() {
const newStoreState = store.getState()
setState(prevState => {
let newSelectedState
let subscriptionCallbackError
try {
newSelectedState = prevState.selector(newStoreState)

if (equalityFn(newSelectedState, prevState.selectedState)) {
// bail out rendering
return prevState
}
} 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
subscriptionCallbackError = err
}

return {
...prevState,
storeState: newStoreState,
selectedState: newSelectedState,
subscriptionCallbackError
}
})
}

const checkForUpdates = () => dispatch(store.getState())
subscription.onStateChange = checkForUpdates
subscription.trySubscribe()

Expand Down

0 comments on commit 32481c9

Please # to comment.