@@ -36,17 +36,28 @@ const ProxyHandlers = {
36
36
* With the native Proxy, all other calls such as access/setting to/of properties will
37
37
* be forwarded to the target Component, so we don't need to copy the Component's
38
38
* own or inherited properties.
39
+ *
40
+ * @see https://github.com/facebook/react/blob/2d80a0cd690bb5650b6c8a6c079a87b5dc42bd15/packages/react-reconciler/src/ReactFiberHooks.old.js#L460
39
41
*/
40
42
apply ( Component : FunctionComponent , thisArg : any , argumentsList : any ) {
41
43
const store = useMemo ( createEffectStore , Empty ) ;
42
44
43
45
useSyncExternalStore ( store . subscribe , store . getSnapshot , store . getSnapshot ) ;
44
46
45
- const ends = store . updater . _start ( ) ;
46
- const kids = Component . apply ( thisArg , argumentsList ) ;
47
- ends ( ) ;
48
-
49
- return kids ;
47
+ const stop = store . updater . _start ( ) ;
48
+
49
+ try {
50
+ const children = Component . apply ( thisArg , argumentsList ) ;
51
+ return children ;
52
+ } catch ( e ) {
53
+ // Re-throwing promises that'll be handled by suspense
54
+ // or an actual error.
55
+ throw e ;
56
+ } finally {
57
+ // Stop effects in either case before return or throw,
58
+ // Otherwise the effect will leak.
59
+ stop ( ) ;
60
+ }
50
61
} ,
51
62
} ;
52
63
@@ -108,10 +119,8 @@ function createEffectStore() {
108
119
updater = this ;
109
120
} ) ;
110
121
updater . _callback = function ( ) {
111
- if ( ! onChangeNotifyReact ) return void unsubscribe ( ) ;
112
-
113
122
version = ( version + 1 ) | 0 ;
114
- onChangeNotifyReact ! ( ) ;
123
+ if ( onChangeNotifyReact ) onChangeNotifyReact ( ) ;
115
124
} ;
116
125
117
126
return {
@@ -121,10 +130,18 @@ function createEffectStore() {
121
130
122
131
return function ( ) {
123
132
/**
124
- * @todo - we may want to unsubscribe here instead?
125
- * Components wrapped in `memo` no longer get updated if we unsubscribe here.
133
+ * Rotate to next version when unsubscribing to ensure that components are re-run
134
+ * when subscribing again.
135
+ *
136
+ * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so
137
+ * don't re-run after subscribing again if the version is the same as last time.
138
+ *
139
+ * Because we unsubscribe from the effect, the version may not change. We simply
140
+ * set a new initial version in case of stale snapshots here.
126
141
*/
142
+ version = ( version + 1 ) | 0 ;
127
143
onChangeNotifyReact = undefined ;
144
+ unsubscribe ( ) ;
128
145
} ;
129
146
} ,
130
147
getSnapshot ( ) {
@@ -142,7 +159,8 @@ function WrapJsx<T>(jsx: T): T {
142
159
}
143
160
144
161
if ( type && typeof type === "object" && type . $$typeof === ReactMemoType ) {
145
- return jsx . call ( jsx , ProxyFunctionalComponent ( type . type ) , props , ...rest ) ;
162
+ type . type = ProxyFunctionalComponent ( type . type ) ;
163
+ return jsx . call ( jsx , type , props , ...rest ) ;
146
164
}
147
165
148
166
if ( typeof type === "string" && props ) {
0 commit comments