diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 61d468bd0726b..1132834e7332e 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -52,7 +52,6 @@ function getPrimitiveStackCache(): Map> { Dispatcher.useState(null); Dispatcher.useReducer((s, a) => s, null); Dispatcher.useRef(null); - Dispatcher.useMutationEffect(() => {}); Dispatcher.useLayoutEffect(() => {}); Dispatcher.useEffect(() => {}); Dispatcher.useImperativeMethods(undefined, () => null); @@ -140,18 +139,6 @@ function useRef(initialValue: T): {current: T} { return ref; } -function useMutationEffect( - create: () => mixed, - inputs: Array | void | null, -): void { - nextHook(); - hookLog.push({ - primitive: 'MutationEffect', - stackError: new Error(), - value: create, - }); -} - function useLayoutEffect( create: () => mixed, inputs: Array | void | null, @@ -221,7 +208,6 @@ const Dispatcher = { useImperativeMethods, useLayoutEffect, useMemo, - useMutationEffect, useReducer, useRef, useState, diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js index ebb3481dfdce2..326c1581fee99 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js @@ -129,7 +129,7 @@ describe('ReactHooksInspection', () => { return result; } function useBaz(value) { - React.useMutationEffect(effect); + React.useLayoutEffect(effect); let result = useCustom(value); return result; } @@ -176,7 +176,7 @@ describe('ReactHooksInspection', () => { value: undefined, subHooks: [ { - name: 'MutationEffect', + name: 'LayoutEffect', value: effect, subHooks: [], }, diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js index bcc903457b6e1..98fa9f7360c29 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js @@ -79,7 +79,6 @@ describe('ReactHooksInspectionIntergration', () => { let [state2, dispatch] = React.useReducer((s, a) => a.value, 'b'); let ref = React.useRef('c'); - React.useMutationEffect(effect); React.useLayoutEffect(effect); React.useEffect(effect); @@ -117,7 +116,6 @@ describe('ReactHooksInspectionIntergration', () => { {name: 'State', value: 'a', subHooks: []}, {name: 'Reducer', value: 'b', subHooks: []}, {name: 'Ref', value: 'c', subHooks: []}, - {name: 'MutationEffect', value: effect, subHooks: []}, {name: 'LayoutEffect', value: effect, subHooks: []}, {name: 'Effect', value: effect, subHooks: []}, {name: 'ImperativeMethods', value: outsideRef.current, subHooks: []}, @@ -134,7 +132,6 @@ describe('ReactHooksInspectionIntergration', () => { {name: 'State', value: 'A', subHooks: []}, {name: 'Reducer', value: 'B', subHooks: []}, {name: 'Ref', value: 'C', subHooks: []}, - {name: 'MutationEffect', value: effect, subHooks: []}, {name: 'LayoutEffect', value: effect, subHooks: []}, {name: 'Effect', value: effect, subHooks: []}, {name: 'ImperativeMethods', value: outsideRef.current, subHooks: []}, diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js index 421a4e074b409..a8f56ef95c571 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js @@ -25,7 +25,6 @@ let useCallback; let useMemo; let useRef; let useImperativeMethods; -let useMutationEffect; let useLayoutEffect; let forwardRef; let yieldedValues; @@ -50,7 +49,6 @@ function initModules() { useMemo = React.useMemo; useRef = React.useRef; useImperativeMethods = React.useImperativeMethods; - useMutationEffect = React.useMutationEffect; useLayoutEffect = React.useLayoutEffect; forwardRef = React.forwardRef; @@ -551,22 +549,6 @@ describe('ReactDOMServerHooks', () => { }); }); - describe('useMutationEffect', () => { - it('should warn when invoked during render', async () => { - function Counter() { - useMutationEffect(() => { - throw new Error('should not be invoked'); - }); - - return ; - } - const domNode = await serverRender(, 1); - expect(clearYields()).toEqual(['Count: 0']); - expect(domNode.tagName).toEqual('SPAN'); - expect(domNode.textContent).toEqual('Count: 0'); - }); - }); - describe('useLayoutEffect', () => { it('should warn when invoked during render', async () => { function Counter() { diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 2fe4dda9d0d0e..3f1d0118228b1 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -271,20 +271,6 @@ function useRef(initialValue: T): {current: T} { } } -function useMutationEffect( - create: () => mixed, - inputs: Array | void | null, -) { - warning( - false, - 'useMutationEffect does nothing on the server, because its effect cannot ' + - "be encoded into the server renderer's output format. This will lead " + - 'to a mismatch between the initial, non-hydrated UI and the intended ' + - 'UI. To avoid this, useMutationEffect should only be used in ' + - 'components that render exclusively on the client.', - ); -} - export function useLayoutEffect( create: () => mixed, inputs: Array | void | null, @@ -358,7 +344,6 @@ export const Dispatcher = { useReducer, useRef, useState, - useMutationEffect, useLayoutEffect, // Callbacks are passed as they are in the server environment. useCallback: identity, diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 82f059fef476e..f525b9ecf0049 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -1087,6 +1087,8 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void { case ForwardRef: case MemoComponent: case SimpleMemoComponent: { + // Note: We currently never use MountMutation, but useLayout uses + // UnmountMutation. commitHookEffectList(UnmountMutation, MountMutation, finishedWork); return; } @@ -1101,6 +1103,8 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void { case ForwardRef: case MemoComponent: case SimpleMemoComponent: { + // Note: We currently never use MountMutation, but useLayout uses + // UnmountMutation. commitHookEffectList(UnmountMutation, MountMutation, finishedWork); return; } diff --git a/packages/react-reconciler/src/ReactFiberDispatcher.js b/packages/react-reconciler/src/ReactFiberDispatcher.js index 26ecac9cc5ff2..f86319e3946fa 100644 --- a/packages/react-reconciler/src/ReactFiberDispatcher.js +++ b/packages/react-reconciler/src/ReactFiberDispatcher.js @@ -15,7 +15,6 @@ import { useImperativeMethods, useLayoutEffect, useMemo, - useMutationEffect, useReducer, useRef, useState, @@ -29,7 +28,6 @@ export const Dispatcher = { useImperativeMethods, useLayoutEffect, useMemo, - useMutationEffect, useReducer, useRef, useState, diff --git a/packages/react-reconciler/src/ReactFiberHooks.js b/packages/react-reconciler/src/ReactFiberHooks.js index c63ead19d983b..3d6de1be19777 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.js +++ b/packages/react-reconciler/src/ReactFiberHooks.js @@ -16,15 +16,12 @@ import {NoWork} from './ReactFiberExpirationTime'; import {enableHooks} from 'shared/ReactFeatureFlags'; import {readContext} from './ReactFiberNewContext'; import { - Snapshot as SnapshotEffect, Update as UpdateEffect, Passive as PassiveEffect, } from 'shared/ReactSideEffectTags'; import { NoEffect as NoHookEffect, - UnmountSnapshot, UnmountMutation, - MountMutation, MountLayout, UnmountPassive, MountPassive, @@ -517,18 +514,6 @@ export function useRef(initialValue: T): {current: T} { return ref; } -export function useMutationEffect( - create: () => mixed, - inputs: Array | void | null, -): void { - useEffectImpl( - SnapshotEffect | UpdateEffect, - UnmountSnapshot | MountMutation, - create, - inputs, - ); -} - export function useLayoutEffect( create: () => mixed, inputs: Array | void | null, @@ -586,26 +571,21 @@ export function useImperativeMethods( // TODO: I've implemented this on top of useEffect because it's almost the // same thing, and it would require an equal amount of code. It doesn't seem // like a common enough use case to justify the additional size. - useEffectImpl( - UpdateEffect, - UnmountMutation | MountLayout, - () => { - if (typeof ref === 'function') { - const refCallback = ref; - const inst = create(); - refCallback(inst); - return () => refCallback(null); - } else if (ref !== null && ref !== undefined) { - const refObject = ref; - const inst = create(); - refObject.current = inst; - return () => { - refObject.current = null; - }; - } - }, - nextInputs, - ); + useLayoutEffect(() => { + if (typeof ref === 'function') { + const refCallback = ref; + const inst = create(); + refCallback(inst); + return () => refCallback(null); + } else if (ref !== null && ref !== undefined) { + const refObject = ref; + const inst = create(); + refObject.current = inst; + return () => { + refObject.current = null; + }; + } + }, nextInputs); } export function useCallback( diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js index b2c5cd07c0e94..36030a2124390 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.internal.js @@ -19,7 +19,6 @@ let SchedulerTracing; let useState; let useReducer; let useEffect; -let useMutationEffect; let useLayoutEffect; let useCallback; let useMemo; @@ -68,7 +67,6 @@ describe('ReactHooksWithNoopRenderer', () => { useState = React.useState; useReducer = React.useReducer; useEffect = React.useEffect; - useMutationEffect = React.useMutationEffect; useLayoutEffect = React.useLayoutEffect; useCallback = React.useCallback; useMemo = React.useMemo; @@ -1260,7 +1258,7 @@ describe('ReactHooksWithNoopRenderer', () => { }); }); - describe('useMutationEffect and useLayoutEffect', () => { + describe('useLayoutEffect', () => { it('fires layout effects after the host has been mutated', () => { function getCommittedText() { const children = ReactNoop.getChildren(); @@ -1286,89 +1284,6 @@ describe('ReactHooksWithNoopRenderer', () => { expect(ReactNoop.getChildren()).toEqual([span(1)]); }); - it('fires mutation effects before layout effects', () => { - let committedText = '(empty)'; - - function Counter(props) { - useMutationEffect(() => { - ReactNoop.yield(`Mount mutation [current: ${committedText}]`); - committedText = props.count + ''; - return () => { - ReactNoop.yield(`Unmount mutation [current: ${committedText}]`); - }; - }); - useLayoutEffect(() => { - ReactNoop.yield(`Mount layout [current: ${committedText}]`); - return () => { - ReactNoop.yield(`Unmount layout [current: ${committedText}]`); - }; - }); - useEffect(() => { - ReactNoop.yield(`Mount normal [current: ${committedText}]`); - return () => { - ReactNoop.yield(`Unmount normal [current: ${committedText}]`); - }; - }); - return null; - } - - ReactNoop.render(); - expect(ReactNoop.flush()).toEqual([ - 'Mount mutation [current: (empty)]', - 'Mount layout [current: 0]', - ]); - expect(committedText).toEqual('0'); - flushPassiveEffects(); - expect(ReactNoop.clearYields()).toEqual(['Mount normal [current: 0]']); - - // Unmount everything - ReactNoop.render(null); - expect(ReactNoop.flush()).toEqual([ - 'Unmount mutation [current: 0]', - 'Unmount layout [current: 0]', - 'Unmount normal [current: 0]', - ]); - }); - - it('force flushes passive effects before firing new mutation effects', () => { - let committedText = '(empty)'; - - function Counter(props) { - useMutationEffect(() => { - ReactNoop.yield(`Mount mutation [current: ${committedText}]`); - committedText = props.count + ''; - return () => { - ReactNoop.yield(`Unmount mutation [current: ${committedText}]`); - }; - }); - useEffect(() => { - ReactNoop.yield(`Mount normal [current: ${committedText}]`); - return () => { - ReactNoop.yield(`Unmount normal [current: ${committedText}]`); - }; - }); - return null; - } - - ReactNoop.render(); - expect(ReactNoop.flush()).toEqual(['Mount mutation [current: (empty)]']); - expect(committedText).toEqual('0'); - - ReactNoop.render(); - expect(ReactNoop.flush()).toEqual([ - 'Mount normal [current: 0]', - 'Unmount mutation [current: 0]', - 'Mount mutation [current: 0]', - ]); - expect(committedText).toEqual('1'); - - flushPassiveEffects(); - expect(ReactNoop.clearYields()).toEqual([ - 'Unmount normal [current: 1]', - 'Mount normal [current: 1]', - ]); - }); - it('force flushes passive effects before firing new layout effects', () => { let committedText = '(empty)'; @@ -1410,96 +1325,6 @@ describe('ReactHooksWithNoopRenderer', () => { 'Mount normal [current: 1]', ]); }); - - it('fires all mutation effects before firing any layout effects', () => { - let committedA = '(empty)'; - let committedB = '(empty)'; - - function CounterA(props) { - useMutationEffect(() => { - ReactNoop.yield( - `Mount A mutation [A: ${committedA}, B: ${committedB}]`, - ); - committedA = props.count + ''; - return () => { - ReactNoop.yield( - `Unmount A mutation [A: ${committedA}, B: ${committedB}]`, - ); - }; - }); - useLayoutEffect(() => { - ReactNoop.yield( - `Mount layout A [A: ${committedA}, B: ${committedB}]`, - ); - return () => { - ReactNoop.yield( - `Unmount layout A [A: ${committedA}, B: ${committedB}]`, - ); - }; - }); - return null; - } - - function CounterB(props) { - useMutationEffect(() => { - ReactNoop.yield( - `Mount B mutation [A: ${committedA}, B: ${committedB}]`, - ); - committedB = props.count + ''; - return () => { - ReactNoop.yield( - `Unmount B mutation [A: ${committedA}, B: ${committedB}]`, - ); - }; - }); - useLayoutEffect(() => { - ReactNoop.yield( - `Mount layout B [A: ${committedA}, B: ${committedB}]`, - ); - return () => { - ReactNoop.yield( - `Unmount layout B [A: ${committedA}, B: ${committedB}]`, - ); - }; - }); - return null; - } - - ReactNoop.render( - - - - , - ); - expect(ReactNoop.flush()).toEqual([ - // All mutation effects fire before all layout effects - 'Mount A mutation [A: (empty), B: (empty)]', - 'Mount B mutation [A: 0, B: (empty)]', - 'Mount layout A [A: 0, B: 0]', - 'Mount layout B [A: 0, B: 0]', - ]); - expect([committedA, committedB]).toEqual(['0', '0']); - - ReactNoop.render( - - - - , - ); - expect(ReactNoop.flush()).toEqual([ - // Note: This shows that the clean-up function of a layout effect is - // fired in the same phase as the set-up function of a mutation. - 'Unmount A mutation [A: 0, B: 0]', - 'Unmount B mutation [A: 0, B: 0]', - 'Mount A mutation [A: 0, B: 0]', - 'Unmount layout A [A: 1, B: 0]', - 'Mount B mutation [A: 1, B: 0]', - 'Unmount layout B [A: 1, B: 1]', - 'Mount layout A [A: 1, B: 1]', - 'Mount layout B [A: 1, B: 1]', - ]); - expect([committedA, committedB]).toEqual(['1', '1']); - }); }); describe('useCallback', () => { diff --git a/packages/react/src/React.js b/packages/react/src/React.js index 872072e29e35a..c19a6653e31bf 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -35,7 +35,6 @@ import { useImperativeMethods, useLayoutEffect, useMemo, - useMutationEffect, useReducer, useRef, useState, @@ -102,7 +101,6 @@ if (enableHooks) { React.useImperativeMethods = useImperativeMethods; React.useLayoutEffect = useLayoutEffect; React.useMemo = useMemo; - React.useMutationEffect = useMutationEffect; React.useReducer = useReducer; React.useRef = useRef; React.useState = useState; diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index e8667672fd02f..18e34ac518af1 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -78,14 +78,6 @@ export function useEffect( return dispatcher.useEffect(create, inputs); } -export function useMutationEffect( - create: () => mixed, - inputs: Array | void | null, -) { - const dispatcher = resolveDispatcher(); - return dispatcher.useMutationEffect(create, inputs); -} - export function useLayoutEffect( create: () => mixed, inputs: Array | void | null,