1
- import { useReducer , useRef , useMemo , useContext } from 'react'
1
+ import { useState , useEffect , useMemo , useContext } from 'react'
2
2
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
3
3
import Subscription from '../utils/Subscription'
4
- import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
5
4
import { ReactReduxContext } from '../components/Context'
6
5
7
6
const refEquality = ( a , b ) => a === b
@@ -12,61 +11,70 @@ function useSelectorWithStoreAndSubscription(
12
11
store ,
13
12
contextSub
14
13
) {
15
- const [ , forceRender ] = useReducer ( s => s + 1 , 0 )
14
+ const [ state , setState ] = useState ( ( ) => ( {
15
+ storeState : store . getState ( ) ,
16
+ selector,
17
+ selectedState : selector ( store . getState ( ) )
18
+ // subscriptionCallbackError: undefined
19
+ } ) )
16
20
17
21
const subscription = useMemo ( ( ) => new Subscription ( store , contextSub ) , [
18
22
store ,
19
23
contextSub
20
24
] )
21
25
22
- const latestSubscriptionCallbackError = useRef ( )
23
- const latestSelector = useRef ( )
24
- const latestSelectedState = useRef ( )
25
-
26
- let selectedState
26
+ let selectedState = state . selectedState
27
27
28
28
try {
29
- if (
30
- selector !== latestSelector . current ||
31
- latestSubscriptionCallbackError . current
32
- ) {
33
- selectedState = selector ( store . getState ( ) )
34
- } else {
35
- selectedState = latestSelectedState . current
29
+ if ( selector !== state . selector || state . subscriptionCallbackError ) {
30
+ const newSelectedState = selector ( state . storeState )
31
+ if ( ! equalityFn ( newSelectedState , selectedState ) ) {
32
+ selectedState = newSelectedState
33
+ // schedule another update
34
+ setState ( prevState => ( {
35
+ ...prevState ,
36
+ selector,
37
+ selectedState
38
+ // subscriptionCallbackError: undefined
39
+ } ) )
40
+ }
36
41
}
37
42
} catch ( err ) {
38
- if ( latestSubscriptionCallbackError . current ) {
39
- err . message += `\nThe error may be correlated with this previous error:\n${ latestSubscriptionCallbackError . current . stack } \n\n`
43
+ if ( state . subscriptionCallbackError ) {
44
+ err . message += `\nThe error may be correlated with this previous error:\n${ state . subscriptionCallbackError . stack } \n\n`
40
45
}
41
46
42
47
throw err
43
48
}
44
49
45
- useIsomorphicLayoutEffect ( ( ) => {
46
- latestSelector . current = selector
47
- latestSelectedState . current = selectedState
48
- latestSubscriptionCallbackError . current = undefined
49
- } )
50
-
51
- useIsomorphicLayoutEffect ( ( ) => {
50
+ useEffect ( ( ) => {
52
51
function checkForUpdates ( ) {
53
- try {
54
- const newSelectedState = latestSelector . current ( store . getState ( ) )
55
-
56
- if ( equalityFn ( newSelectedState , latestSelectedState . current ) ) {
57
- return
52
+ const newStoreState = store . getState ( )
53
+ setState ( prevState => {
54
+ let newSelectedState
55
+ let subscriptionCallbackError
56
+ try {
57
+ newSelectedState = prevState . selector ( newStoreState )
58
+
59
+ if ( equalityFn ( newSelectedState , prevState . selectedState ) ) {
60
+ // bail out rendering
61
+ return prevState
62
+ }
63
+ } catch ( err ) {
64
+ // we ignore all errors here, since when the component
65
+ // is re-rendered, the selectors are called again, and
66
+ // will throw again, if neither props nor store state
67
+ // changed
68
+ subscriptionCallbackError = err
58
69
}
59
70
60
- latestSelectedState . current = newSelectedState
61
- } catch ( err ) {
62
- // we ignore all errors here, since when the component
63
- // is re-rendered, the selectors are called again, and
64
- // will throw again, if neither props nor store state
65
- // changed
66
- latestSubscriptionCallbackError . current = err
67
- }
68
-
69
- forceRender ( { } )
71
+ return {
72
+ ...prevState ,
73
+ storeState : newStoreState ,
74
+ selectedState : newSelectedState ,
75
+ subscriptionCallbackError
76
+ }
77
+ } )
70
78
}
71
79
72
80
subscription . onStateChange = checkForUpdates
0 commit comments