@@ -18,6 +18,7 @@ import {
18
18
enableProfilerNestedUpdatePhase ,
19
19
enableSchedulingProfiler ,
20
20
enableScopeAPI ,
21
+ enableUseResourceEffectHook ,
21
22
} from 'shared/ReactFeatureFlags' ;
22
23
import {
23
24
ClassComponent ,
@@ -49,6 +50,7 @@ import {
49
50
Layout as HookLayout ,
50
51
Insertion as HookInsertion ,
51
52
Passive as HookPassive ,
53
+ HasEffect as HookHasEffect ,
52
54
} from './ReactHookEffectTags' ;
53
55
import { didWarnAboutReassigningProps } from './ReactFiberBeginWork' ;
54
56
import {
@@ -70,6 +72,10 @@ import {
70
72
} from './ReactFiberCallUserSpace' ;
71
73
72
74
import { runWithFiberInDEV } from './ReactCurrentFiber' ;
75
+ import {
76
+ ResourceEffectIdentityKind ,
77
+ ResourceEffectUpdateKind ,
78
+ } from './ReactFiberHooks' ;
73
79
74
80
function shouldProfile ( current : Fiber ) : boolean {
75
81
return (
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146
152
147
153
// Mount
148
154
let destroy ;
155
+ if ( enableUseResourceEffectHook ) {
156
+ if ( effect . resourceKind === ResourceEffectIdentityKind ) {
157
+ if ( __DEV__ ) {
158
+ effect . inst . resource = runWithFiberInDEV (
159
+ finishedWork ,
160
+ callCreateInDEV ,
161
+ effect ,
162
+ ) ;
163
+ if ( effect . inst . resource == null ) {
164
+ console . error (
165
+ 'useResourceEffect must provide a callback which returns a resource. ' +
166
+ 'If a managed resource is not needed here, use useEffect. Received %s' ,
167
+ effect . inst . resource ,
168
+ ) ;
169
+ }
170
+ } else {
171
+ effect . inst . resource = effect . create ( ) ;
172
+ }
173
+ destroy = effect . inst . destroy ;
174
+ }
175
+ if ( effect . resourceKind === ResourceEffectUpdateKind ) {
176
+ if (
177
+ // We don't want to fire updates on remount during Activity
178
+ ( flags & HookHasEffect ) > 0 &&
179
+ typeof effect . update === 'function' &&
180
+ effect . inst . resource != null
181
+ ) {
182
+ // TODO(@poteto) what about multiple updates?
183
+ if ( __DEV__ ) {
184
+ runWithFiberInDEV ( finishedWork , callCreateInDEV , effect ) ;
185
+ } else {
186
+ effect . update ( effect . inst . resource ) ;
187
+ }
188
+ }
189
+ }
190
+ }
149
191
if ( __DEV__ ) {
150
192
if ( ( flags & HookInsertion ) !== NoHookEffect ) {
151
193
setIsRunningInsertionEffect ( true ) ;
152
194
}
153
- destroy = runWithFiberInDEV ( finishedWork , callCreateInDEV , effect ) ;
195
+ if ( enableUseResourceEffectHook ) {
196
+ if ( effect . resourceKind == null ) {
197
+ destroy = runWithFiberInDEV (
198
+ finishedWork ,
199
+ callCreateInDEV ,
200
+ effect ,
201
+ ) ;
202
+ }
203
+ } else {
204
+ destroy = runWithFiberInDEV (
205
+ finishedWork ,
206
+ callCreateInDEV ,
207
+ effect ,
208
+ ) ;
209
+ }
154
210
if ( ( flags & HookInsertion ) !== NoHookEffect ) {
155
211
setIsRunningInsertionEffect ( false ) ;
156
212
}
157
213
} else {
158
- const create = effect . create ;
159
- const inst = effect . inst ;
160
- destroy = create ( ) ;
161
- inst . destroy = destroy ;
214
+ if ( enableUseResourceEffectHook ) {
215
+ if ( effect . resourceKind == null ) {
216
+ const create = effect . create ;
217
+ const inst = effect . inst ;
218
+ destroy = create ( ) ;
219
+ inst . destroy = destroy ;
220
+ }
221
+ } else {
222
+ if ( effect . resourceKind != null ) {
223
+ if ( __DEV__ ) {
224
+ console . error (
225
+ 'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, ' +
226
+ 'got %s' ,
227
+ effect . resourceKind ,
228
+ ) ;
229
+ }
230
+ }
231
+ const create = effect . create ;
232
+ const inst = effect . inst ;
233
+ // $FlowFixMe[incompatible-type] (@poteto)
234
+ // $FlowFixMe[not-a-function] (@poteto)
235
+ destroy = create ( ) ;
236
+ // $FlowFixMe[incompatible-type] (@poteto)
237
+ inst . destroy = destroy ;
238
+ }
162
239
}
163
240
164
241
if ( enableSchedulingProfiler ) {
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176
253
hookName = 'useLayoutEffect' ;
177
254
} else if ( ( effect . tag & HookInsertion ) !== NoFlags ) {
178
255
hookName = 'useInsertionEffect' ;
256
+ } else if (
257
+ enableUseResourceEffectHook &&
258
+ effect . resourceKind != null
259
+ ) {
260
+ hookName = 'useResourceEffect' ;
179
261
} else {
180
262
hookName = 'useEffect' ;
181
263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202
284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
203
285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching' ;
204
286
} else {
287
+ // $FlowFixMe[unsafe-addition] (@poteto)
205
288
addendum = ' You returned: ' + destroy ;
206
289
}
207
290
runWithFiberInDEV (
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246
329
const inst = effect . inst ;
247
330
const destroy = inst . destroy ;
248
331
if ( destroy !== undefined ) {
249
- inst . destroy = undefined ;
332
+ if ( enableUseResourceEffectHook ) {
333
+ if ( effect . resourceKind == null ) {
334
+ inst . destroy = undefined ;
335
+ }
336
+ } else {
337
+ inst . destroy = undefined ;
338
+ }
250
339
if ( enableSchedulingProfiler ) {
251
340
if ( ( flags & HookPassive ) !== NoHookEffect ) {
252
341
markComponentPassiveEffectUnmountStarted ( finishedWork ) ;
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260
349
setIsRunningInsertionEffect ( true ) ;
261
350
}
262
351
}
263
- safelyCallDestroy ( finishedWork , nearestMountedAncestor , destroy ) ;
352
+ if ( enableUseResourceEffectHook ) {
353
+ if (
354
+ effect . resourceKind === ResourceEffectIdentityKind &&
355
+ effect . inst . resource != null
356
+ ) {
357
+ safelyCallDestroyWithResource (
358
+ finishedWork ,
359
+ nearestMountedAncestor ,
360
+ destroy ,
361
+ effect . inst . resource ,
362
+ ) ;
363
+ if ( effect . next . resourceKind === ResourceEffectUpdateKind ) {
364
+ // $FlowFixMe[prop-missing] (@poteto)
365
+ effect . next . update = undefined ;
366
+ } else {
367
+ if ( __DEV__ ) {
368
+ console . error (
369
+ 'Expected a ResourceEffectUpdateKind to follow ResourceEffectIdentityKind, ' +
370
+ 'got %s. This is a bug in React.' ,
371
+ effect . next . resourceKind ,
372
+ ) ;
373
+ }
374
+ }
375
+ effect . inst . resource = null ;
376
+ }
377
+ if ( effect . resourceKind == null ) {
378
+ safelyCallDestroy (
379
+ finishedWork ,
380
+ nearestMountedAncestor ,
381
+ destroy ,
382
+ ) ;
383
+ }
384
+ } else {
385
+ safelyCallDestroy ( finishedWork , nearestMountedAncestor , destroy ) ;
386
+ }
264
387
if ( __DEV__ ) {
265
388
if ( ( flags & HookInsertion ) !== NoHookEffect ) {
266
389
setIsRunningInsertionEffect ( false ) ;
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
895
1018
}
896
1019
}
897
1020
1021
+ function safelyCallDestroyWithResource (
1022
+ current : Fiber ,
1023
+ nearestMountedAncestor : Fiber | null ,
1024
+ destroy : mixed => void ,
1025
+ resource : mixed ,
1026
+ ) {
1027
+ const destroy_ = resource == null ? destroy : destroy . bind ( null , resource ) ;
1028
+ if ( __DEV__ ) {
1029
+ runWithFiberInDEV (
1030
+ current ,
1031
+ callDestroyInDEV ,
1032
+ current ,
1033
+ nearestMountedAncestor ,
1034
+ destroy_ ,
1035
+ ) ;
1036
+ } else {
1037
+ try {
1038
+ destroy_ ( ) ;
1039
+ } catch ( error ) {
1040
+ captureCommitPhaseError ( current , nearestMountedAncestor , error ) ;
1041
+ }
1042
+ }
1043
+ }
1044
+
898
1045
function commitProfiler(
899
1046
finishedWork: Fiber,
900
1047
current: Fiber | null,
0 commit comments