1
- import { useReducer , useRef , useEffect , useMemo , useLayoutEffect } from 'react'
1
+ import { useState , useRef , useEffect , useMemo , useLayoutEffect } from 'react'
2
2
import invariant from 'invariant'
3
3
import { useReduxContext } from './useReduxContext'
4
4
import shallowEqual from '../utils/shallowEqual'
@@ -42,52 +42,36 @@ export function useSelector(selector) {
42
42
invariant ( selector , `You must pass a selector to useSelectors` )
43
43
44
44
const { store, subscription : contextSub } = useReduxContext ( )
45
- const [ , forceRender ] = useReducer ( s => s + 1 , 0 )
45
+ const [ subscriptionResult , setSubscriptionResult ] = useState ( [ null ] )
46
46
47
47
const subscription = useMemo ( ( ) => new Subscription ( store , contextSub ) , [
48
48
store ,
49
49
contextSub
50
50
] )
51
51
52
+ const latestSelector = useRef ( )
53
+ const latestResult = useRef ( )
52
54
const latestSubscriptionCallbackError = useRef ( )
53
- const latestSelector = useRef ( selector )
55
+ const latestSubscriptionResult = useRef ( subscriptionResult )
54
56
55
- let selectedState = undefined
56
-
57
- try {
58
- selectedState = selector ( store . getState ( ) )
59
- } catch ( err ) {
60
- let errorMessage = `An error occured while selecting the store state: ${
61
- err . message
62
- } .`
63
-
64
- if ( latestSubscriptionCallbackError . current ) {
65
- errorMessage += `\nThe error may be correlated with this previous error:\n${
66
- latestSubscriptionCallbackError . current . stack
67
- } \n\nOriginal stack trace:`
68
- }
69
-
70
- throw new Error ( errorMessage )
71
- }
72
-
73
- const latestSelectedState = useRef ( selectedState )
57
+ let result = latestResult . current
74
58
75
59
useIsomorphicLayoutEffect ( ( ) => {
60
+ latestResult . current = result
76
61
latestSelector . current = selector
77
- latestSelectedState . current = selectedState
78
62
latestSubscriptionCallbackError . current = undefined
63
+ latestSubscriptionResult . current = subscriptionResult
79
64
} )
80
65
81
66
useIsomorphicLayoutEffect ( ( ) => {
82
67
function checkForUpdates ( ) {
68
+ let newSelectedState
83
69
try {
84
- const newSelectedState = latestSelector . current ( store . getState ( ) )
70
+ newSelectedState = latestSelector . current ( store . getState ( ) )
85
71
86
- if ( shallowEqual ( newSelectedState , latestSelectedState . current ) ) {
72
+ if ( shallowEqual ( newSelectedState , latestResult . current ) ) {
87
73
return
88
74
}
89
-
90
- latestSelectedState . current = newSelectedState
91
75
} catch ( err ) {
92
76
// we ignore all errors here, since when the component
93
77
// is re-rendered, the selectors are called again, and
@@ -96,7 +80,9 @@ export function useSelector(selector) {
96
80
latestSubscriptionCallbackError . current = err
97
81
}
98
82
99
- forceRender ( { } )
83
+ const newSubscriptionResult = new Array ( 1 )
84
+ newSubscriptionResult [ 0 ] = newSelectedState
85
+ setSubscriptionResult ( newSubscriptionResult )
100
86
}
101
87
102
88
subscription . onStateChange = checkForUpdates
@@ -107,5 +93,25 @@ export function useSelector(selector) {
107
93
return ( ) => subscription . tryUnsubscribe ( )
108
94
} , [ store , subscription ] )
109
95
110
- return selectedState
96
+ try {
97
+ return ( result =
98
+ selector !== latestSelector . current ||
99
+ latestSubscriptionCallbackError . current
100
+ ? selector ( store . getState ( ) )
101
+ : subscriptionResult !== latestSubscriptionResult . current
102
+ ? subscriptionResult [ 0 ]
103
+ : result )
104
+ } catch ( err ) {
105
+ let errorMessage = `An error occured while selecting the store state: ${
106
+ err . message
107
+ } .`
108
+
109
+ if ( latestSubscriptionCallbackError . current ) {
110
+ errorMessage += `\nThe error may be correlated with this previous error:\n${
111
+ latestSubscriptionCallbackError . current . stack
112
+ } \n\nOriginal stack trace:`
113
+ }
114
+
115
+ throw new Error ( errorMessage )
116
+ }
111
117
}
0 commit comments