Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [internal] revert change merged accidentally by rickhanlonii · Pull Request #35546 · react/react · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,6 @@ import type {ViewTransitionState} from './ReactFiberViewTransitionComponent';
import {
alwaysThrottleRetries,
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableProfilerTimer,
enableProfilerCommitHooks,
Expand DownExpand Up@@ -500,7 +499,7 @@ function commitBeforeMutationEffectsOnFiber(
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
if (!enableEffectEventMutationPhase && (flags & Update) !== NoFlags) {
if ((flags & Update) !== NoFlags) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads = updateQueue !== null ? updateQueue.events : null;
Expand DownExpand Up@@ -2047,19 +2046,6 @@ function commitMutationEffectsOnFiber(
commitReconciliationEffects(finishedWork, lanes);

if (flags & Update) {
// Mutate event effect callbacks before insertion effects.
if (enableEffectEventMutationPhase) {
const updateQueue: FunctionComponentUpdateQueue | null =
(finishedWork.updateQueue: any);
const eventPayloads =
updateQueue !== null ? updateQueue.events : null;
if (eventPayloads !== null) {
for (let ii = 0; ii < eventPayloads.length; ii++) {
const {ref, nextImpl} = eventPayloads[ii];
ref.impl = nextImpl;
}
}
}
commitHookEffectListUnmount(
HookInsertion | HookHasEffect,
finishedWork,
Expand Down
14 changes: 5 additions & 9 deletions packages/react-reconciler/src/ReactFiberFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,7 @@
* @flow
*/

import {
enableCreateEventHandleAPI,
enableEffectEventMutationPhase,
} from 'shared/ReactFeatureFlags';
import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';

export type Flags = number;

Expand DownExpand Up@@ -102,11 +99,10 @@ export const BeforeMutationMask: number =
// TODO: Only need to visit Deletions during BeforeMutation phase if an
// element is focused.
Update | ChildDeletion | Visibility
: // useEffectEvent uses the snapshot phase,
// but we're moving it to the mutation phase.
enableEffectEventMutationPhase
? 0
: Update);
: // TODO: The useEffectEvent hook uses the snapshot phase for clean up but it
// really should use the mutation phase for this or at least schedule an
// explicit Snapshot phase flag for this.
Update);

// For View Transition support we use the snapshot phase to scan the tree for potentially
// affected ViewTransition components.
Expand Down
208 changes: 0 additions & 208 deletions packages/react-reconciler/src/__tests__/useEffectEvent-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -595,214 +595,6 @@ describe('useEffectEvent', () => {
assertLog(['Effect value: 2', 'Event value: 2']);
});

it('updates parent and child event effects before their respective effect lifecycles', async () => {
function Parent({value}) {
const parentEvent = useEffectEvent(() => {
Scheduler.log('Parent event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Parent insertion');
parentEvent();
}, [value]);

return <Child value={value} />;
}

function Child({value}) {
const childEvent = useEffectEvent(() => {
Scheduler.log('Child event: ' + value);
});

useInsertionEffect(() => {
Scheduler.log('Child insertion');
childEvent();
}, [value]);

return null;
}

ReactNoop.render(<Parent value={1} />);
await waitForAll([
'Child insertion',
'Child event: 1',
'Parent insertion',
'Parent event: 1',
]);

await act(() => ReactNoop.render(<Parent value={2} />));
// Each component's event is updated before its own insertion effect runs
assertLog([
'Child insertion',
'Child event: 2',
'Parent insertion',
'Parent event: 2',
]);
});

it('fires all insertion effects (interleaved) with useEffectEvent before firing any layout effects', async () => {
// This test mirrors the 'fires all insertion effects (interleaved) before firing any layout effects'
// test in ReactHooksWithNoopRenderer-test.js, but adds useEffectEvent to verify that
// event payloads are updated before each component's insertion effects run.
// It also includes passive effects to verify the full effect lifecycle.
let committedA = '(empty)';
let committedB = '(empty)';

function CounterA(props) {
const onEvent = useEffectEvent(() => {
return `Event A [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedA = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive A [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

function CounterB(props) {
const onEvent = useEffectEvent(() => {
return `Event B [A: ${committedA}, B: ${committedB}]`;
});

useInsertionEffect(() => {
// Call the event function to verify it sees the latest value
Scheduler.log(
`Create Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
committedB = String(props.count);
return () => {
Scheduler.log(
`Destroy Insertion B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useLayoutEffect(() => {
Scheduler.log(
`Create Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Layout B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

useEffect(() => {
Scheduler.log(
`Create Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
return () => {
Scheduler.log(
`Destroy Passive B [A: ${committedA}, B: ${committedB}], event: ${onEvent()}`,
);
};
});

return null;
}

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={0} />
<CounterB count={0} />
</React.Fragment>,
);
// All insertion effects fire before all layout effects, then passive effects
// Event functions should see the state AT THE TIME they're called
await waitForAll([
// Insertion effects (mutation phase)
'Create Insertion A [A: (empty), B: (empty)], event: Event A [A: (empty), B: (empty)]',
'Create Insertion B [A: 0, B: (empty)], event: Event B [A: 0, B: (empty)]',
// Layout effects
'Create Layout A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Layout B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
// Passive effects
'Create Passive A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Passive B [A: 0, B: 0], event: Event B [A: 0, B: 0]',
]);
expect([committedA, committedB]).toEqual(['0', '0']);
});

await act(async () => {
ReactNoop.render(
<React.Fragment>
<CounterA count={1} />
<CounterB count={1} />
</React.Fragment>,
);
await waitForAll([
// Component A: insertion destroy, then create
'Destroy Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
'Create Insertion A [A: 0, B: 0], event: Event A [A: 0, B: 0]',
// Component A: layout destroy (after insertion updated committedA)
'Destroy Layout A [A: 1, B: 0], event: Event A [A: 1, B: 0]',
// Component B: insertion destroy, then create
'Destroy Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
'Create Insertion B [A: 1, B: 0], event: Event B [A: 1, B: 0]',
// Component B: layout destroy (after insertion updated committedB)
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Layout creates
'Create Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys then creates
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Create Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Create Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
expect([committedA, committedB]).toEqual(['1', '1']);
});

// Unmount everything
await act(async () => {
ReactNoop.render(null);
await waitForAll([
// Insertion and layout destroys (mutation/layout phase)
'Destroy Insertion A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Layout A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Insertion B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
'Destroy Layout B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
// Passive destroys
'Destroy Passive A [A: 1, B: 1], event: Event A [A: 1, B: 1]',
'Destroy Passive B [A: 1, B: 1], event: Event B [A: 1, B: 1]',
]);
});
});

it("doesn't provide a stable identity", async () => {
function Counter({shouldRender, value}) {
const onClick = useEffectEvent(() => {
Expand Down
4 changes: 0 additions & 4 deletions packages/shared/ReactFeatureFlags.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -125,10 +125,6 @@ export const enableFizzExternalRuntime = __EXPERIMENTAL__;

export const alwaysThrottleRetries: boolean = true;

// Gate whether useEffectEvent uses the mutation phase (true) or before-mutation
// phase (false) for updating event function references.
export const enableEffectEventMutationPhase: boolean = false;

export const passChildrenWhenCloningPersistedNodes: boolean = false;

export const enableEagerAlternateStateNodeCleanup: boolean = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,4 +27,3 @@ export const enableFragmentRefs = __VARIANT__;
export const enableFragmentRefsScrollIntoView = __VARIANT__;
export const enableFragmentRefsInstanceHandles = __VARIANT__;
export const enableComponentPerformanceTrack = __VARIANT__;
export const enableEffectEventMutationPhase = __VARIANT__;
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-fb.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,6 @@ const dynamicFlags: DynamicExportsType = (dynamicFlagsUntyped: any);
// the exports object every time a flag is read.
export const {
alwaysThrottleRetries,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableObjectFiber,
enableEagerAlternateStateNodeCleanup,
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.native-oss.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,6 @@ export const enableSchedulingProfiler: boolean =
!enableComponentPerformanceTrack && __PROFILE__;
export const enableScopeAPI: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;
export const enableSuspenseAvoidThisFallback: boolean = false;
export const enableSuspenseCallback: boolean = false;
export const enableTaint: boolean = true;
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.test-renderer.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -58,7 +58,6 @@ export const enableInfiniteRenderLoopDetection: boolean = false;

export const renameElementSymbol: boolean = true;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableYieldingBeforePassive: boolean = true;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,7 +43,6 @@ export const enableComponentPerformanceTrack = false;
export const enablePerformanceIssueReporting = false;
export const enableScopeAPI = false;
export const enableEagerAlternateStateNodeCleanup = true;
export const enableEffectEventMutationPhase = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,7 +64,6 @@ export const renameElementSymbol: boolean = false;

export const enableObjectFiber: boolean = false;
export const enableEagerAlternateStateNodeCleanup: boolean = true;
export const enableEffectEventMutationPhase: boolean = false;

export const enableHydrationLaneScheduling: boolean = true;

Expand Down
2 changes: 0 additions & 2 deletions packages/shared/forks/ReactFeatureFlags.www-dynamic.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,8 +40,6 @@ export const enableAsyncDebugInfo: boolean = __VARIANT__;

export const enableInternalInstanceMap: boolean = __VARIANT__;

export const enableEffectEventMutationPhase: boolean = __VARIANT__;

// TODO: These flags are hard-coded to the default values used in open source.
// Update the tests so that they pass in either mode, then set these
// to __VARIANT__.
Expand Down
1 change: 0 additions & 1 deletion packages/shared/forks/ReactFeatureFlags.www.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,6 @@ export const {
alwaysThrottleRetries,
disableLegacyContextForFunctionComponents,
disableSchedulerTimeoutInWorkLoop,
enableEffectEventMutationPhase,
enableHiddenSubtreeInsertionEffectCleanup,
enableInfiniteRenderLoopDetection,
enableNoCloningMemoCache,
Expand Down
Loading