Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Commit 047d95e

Browse files
authored
[crud] Basic implementation (#31523)
This PR introduces a new experimental hook `useResourceEffect`, which is something that we're doing some very early initial tests on. This may likely not pan out and will be removed or modified if so. Please do not rely on it as it will break.
1 parent 92c0f5f commit 047d95e

9 files changed

Lines changed: 1309 additions & 33 deletions

‎packages/react-reconciler/src/ReactFiberCallUserSpace.js‎

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import type {CapturedValue} from './ReactCapturedValue';
1414

1515
import{isRendering,setIsRendering}from'./ReactCurrentFiber';
1616
import{captureCommitPhaseError}from'./ReactFiberWorkLoop';
17+
import{
18+
ResourceEffectIdentityKind,
19+
ResourceEffectUpdateKind,
20+
}from'./ReactFiberHooks';
21+
import{enableUseResourceEffectHook}from'shared/ReactFeatureFlags';
1722

1823
// These indirections exists so we can exclude its stack frame in DEV (and anything below it).
1924
// TODO: Consider marking the whole bundle instead of these boundaries.
@@ -176,12 +181,54 @@ export const callComponentWillUnmountInDEV: (
176181
: (null: any);
177182

178183
constcallCreate={
179-
'react-stack-bottom-frame': function(effect: Effect): (()=>void)|void{
180-
const create =effect.create;
181-
constinst=effect.inst;
182-
constdestroy=create();
183-
inst.destroy=destroy;
184-
returndestroy;
184+
'react-stack-bottom-frame': function(
185+
effect: Effect,
186+
): (()=>void)|mixed|void{
187+
if(!enableUseResourceEffectHook){
188+
if(effect.resourceKind!=null){
189+
if(__DEV__){
190+
console.error(
191+
'Expected only SimpleEffects when enableUseResourceEffectHook is disabled, '+
192+
'got %s',
193+
effect.resourceKind,
194+
);
195+
}
196+
}
197+
constcreate=effect.create;
198+
constinst=effect.inst;
199+
// $FlowFixMe[not-a-function] (@poteto)
200+
constdestroy=create();
201+
// $FlowFixMe[incompatible-type] (@poteto)
202+
inst.destroy=destroy;
203+
returndestroy;
204+
}else{
205+
if(effect.resourceKind==null){
206+
constcreate=effect.create;
207+
constinst=effect.inst;
208+
constdestroy=create();
209+
inst.destroy=destroy;
210+
returndestroy;
211+
}
212+
switch(effect.resourceKind){
213+
caseResourceEffectIdentityKind: {
214+
returneffect.create();
215+
}
216+
caseResourceEffectUpdateKind: {
217+
if(typeofeffect.update==='function'){
218+
effect.update(effect.inst.resource);
219+
}
220+
break;
221+
}
222+
default: {
223+
if(__DEV__){
224+
console.error(
225+
'Unhandled Effect kind %s. This is a bug in React.',
226+
effect.kind,
227+
);
228+
}
229+
}
230+
}
231+
}
185232
},
186233
};
187234

‎packages/react-reconciler/src/ReactFiberCommitEffects.js‎

Lines changed: 154 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
enableProfilerNestedUpdatePhase,
1919
enableSchedulingProfiler,
2020
enableScopeAPI,
21+
enableUseResourceEffectHook,
2122
}from'shared/ReactFeatureFlags';
2223
import{
2324
ClassComponent,
@@ -49,6 +50,7 @@ import {
4950
LayoutasHookLayout,
5051
InsertionasHookInsertion,
5152
PassiveasHookPassive,
53+
HasEffectasHookHasEffect,
5254
}from'./ReactHookEffectTags';
5355
import{didWarnAboutReassigningProps}from'./ReactFiberBeginWork';
5456
import{
@@ -70,6 +72,10 @@ import {
7072
}from'./ReactFiberCallUserSpace';
7173

7274
import{runWithFiberInDEV}from'./ReactCurrentFiber';
75+
import{
76+
ResourceEffectIdentityKind,
77+
ResourceEffectUpdateKind,
78+
}from'./ReactFiberHooks';
7379

7480
functionshouldProfile(current: Fiber): boolean{
7581
return(
@@ -146,19 +152,90 @@ export function commitHookEffectListMount(
146152

147153
// Mount
148154
letdestroy;
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+
typeofeffect.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+
}
149191
if(__DEV__){
150192
if((flags&HookInsertion)!==NoHookEffect){
151193
setIsRunningInsertionEffect(true);
152194
}
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+
}
154210
if((flags&HookInsertion)!==NoHookEffect){
155211
setIsRunningInsertionEffect(false);
156212
}
157213
}else{
158-
constcreate=effect.create;
159-
constinst=effect.inst;
160-
destroy=create();
161-
inst.destroy=destroy;
214+
if(enableUseResourceEffectHook){
215+
if(effect.resourceKind==null){
216+
constcreate=effect.create;
217+
constinst=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+
constcreate=effect.create;
232+
constinst=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+
}
162239
}
163240

164241
if(enableSchedulingProfiler){
@@ -176,6 +253,11 @@ export function commitHookEffectListMount(
176253
hookName='useLayoutEffect';
177254
}elseif((effect.tag&HookInsertion)!==NoFlags){
178255
hookName='useInsertionEffect';
256+
}elseif(
257+
enableUseResourceEffectHook&&
258+
effect.resourceKind!=null
259+
){
260+
hookName='useResourceEffect';
179261
}else{
180262
hookName='useEffect';
181263
}
@@ -202,6 +284,7 @@ export function commitHookEffectListMount(
202284
`}, [someId]); // Or [] if effect doesn't need props or state\n\n`+
203285
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching';
204286
}else{
287+
// $FlowFixMe[unsafe-addition] (@poteto)
205288
addendum=' You returned: '+destroy;
206289
}
207290
runWithFiberInDEV(
@@ -246,7 +329,13 @@ export function commitHookEffectListUnmount(
246329
constinst=effect.inst;
247330
constdestroy=inst.destroy;
248331
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+
}
250339
if(enableSchedulingProfiler){
251340
if((flags&HookPassive)!==NoHookEffect){
252341
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -260,7 +349,41 @@ export function commitHookEffectListUnmount(
260349
setIsRunningInsertionEffect(true);
261350
}
262351
}
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+
}
264387
if(__DEV__){
265388
if((flags&HookInsertion)!==NoHookEffect){
266389
setIsRunningInsertionEffect(false);
@@ -895,6 +1018,30 @@ function safelyCallDestroy(
8951018
}
8961019
}
8971020

1021+
functionsafelyCallDestroyWithResource(
1022+
current: Fiber,
1023+
nearestMountedAncestor: Fiber|null,
1024+
destroy: mixed=>void,
1025+
resource: mixed,
1026+
){
1027+
constdestroy_=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+
8981045
function commitProfiler(
8991046
finishedWork: Fiber,
9001047
current: Fiber | null,

0 commit comments

Comments
 (0)