mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Skip updating effect tag when skipping effect
For example, if you have `useEffect(..., [])`, there's no need to set .effectTag to `Update | Passive` on updates.
This commit is contained in:
committed by
Andrew Clark
parent
9f34eb79a3
commit
5fc84efacc
@@ -24,7 +24,7 @@ let useContext;
|
||||
let useCallback;
|
||||
let useMemo;
|
||||
let useRef;
|
||||
let useAPI;
|
||||
let useImperativeMethods;
|
||||
let useMutationEffect;
|
||||
let useLayoutEffect;
|
||||
let forwardRef;
|
||||
@@ -49,7 +49,7 @@ function initModules() {
|
||||
useCallback = React.useCallback;
|
||||
useMemo = React.useMemo;
|
||||
useRef = React.useRef;
|
||||
useAPI = React.useAPI;
|
||||
useImperativeMethods = React.useImperativeMethods;
|
||||
useMutationEffect = React.useMutationEffect;
|
||||
useLayoutEffect = React.useLayoutEffect;
|
||||
forwardRef = React.forwardRef;
|
||||
@@ -518,10 +518,10 @@ describe('ReactDOMServerHooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('useAPI', () => {
|
||||
describe('useImperativeMethods', () => {
|
||||
it('should not be invoked on the server', async () => {
|
||||
function Counter(props, ref) {
|
||||
useAPI(ref, () => {
|
||||
useImperativeMethods(ref, () => {
|
||||
throw new Error('should not be invoked');
|
||||
});
|
||||
return <Text text={props.label + ': ' + ref.current} />;
|
||||
|
||||
@@ -365,8 +365,8 @@ export const Dispatcher = {
|
||||
useState,
|
||||
useMutationEffect,
|
||||
useLayoutEffect,
|
||||
// useAPI is not run in the server environment
|
||||
useAPI: noop,
|
||||
// useImperativeMethods is not run in the server environment
|
||||
useImperativeMethods: noop,
|
||||
// Callbacks are not run in the server environment.
|
||||
useCallback: noop,
|
||||
// Effects are not run in the server environment.
|
||||
|
||||
+2
-2
@@ -9,10 +9,10 @@
|
||||
|
||||
import {readContext} from './ReactFiberNewContext';
|
||||
import {
|
||||
useAPI,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeMethods,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useMutationEffect,
|
||||
@@ -23,10 +23,10 @@ import {
|
||||
|
||||
export const Dispatcher = {
|
||||
readContext,
|
||||
useAPI,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeMethods,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useMutationEffect,
|
||||
|
||||
+14
-15
@@ -179,9 +179,7 @@ export function finishHooks(
|
||||
|
||||
renderedWork.memoizedState = firstWorkInProgressHook;
|
||||
renderedWork.expirationTime = remainingExpirationTime;
|
||||
if (componentUpdateQueue !== null) {
|
||||
renderedWork.updateQueue = (componentUpdateQueue: any);
|
||||
}
|
||||
renderedWork.updateQueue = (componentUpdateQueue: any);
|
||||
|
||||
const didRenderTooFewHooks =
|
||||
currentHook !== null && currentHook.next !== null;
|
||||
@@ -576,26 +574,27 @@ function useEffectImpl(fiberEffectTag, hookEffectTag, create, inputs): void {
|
||||
currentlyRenderingFiber = resolveCurrentlyRenderingFiber();
|
||||
workInProgressHook = createWorkInProgressHook();
|
||||
|
||||
let nextEffect;
|
||||
let nextInputs = inputs !== undefined && inputs !== null ? inputs : [create];
|
||||
let destroy = null;
|
||||
if (currentHook !== null) {
|
||||
const prevEffect = currentHook.memoizedState;
|
||||
const prevInputs = prevEffect.inputs;
|
||||
nextEffect = pushEffect(
|
||||
inputsAreEqual(nextInputs, prevInputs) ? NoHookEffect : hookEffectTag,
|
||||
create,
|
||||
prevEffect.destroy,
|
||||
nextInputs,
|
||||
);
|
||||
} else {
|
||||
nextEffect = pushEffect(hookEffectTag, create, null, nextInputs);
|
||||
destroy = prevEffect.destroy;
|
||||
if (inputsAreEqual(nextInputs, prevEffect.inputs)) {
|
||||
pushEffect(NoHookEffect, create, destroy, nextInputs);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
workInProgressHook.memoizedState = nextEffect;
|
||||
currentlyRenderingFiber.effectTag |= fiberEffectTag;
|
||||
workInProgressHook.memoizedState = pushEffect(
|
||||
hookEffectTag,
|
||||
create,
|
||||
destroy,
|
||||
nextInputs,
|
||||
);
|
||||
}
|
||||
|
||||
export function useAPI<T>(
|
||||
export function useImperativeMethods<T>(
|
||||
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
|
||||
create: () => T,
|
||||
inputs: Array<mixed> | void | null,
|
||||
|
||||
@@ -24,7 +24,7 @@ let useLayoutEffect;
|
||||
let useCallback;
|
||||
let useMemo;
|
||||
let useRef;
|
||||
let useAPI;
|
||||
let useImperativeMethods;
|
||||
let forwardRef;
|
||||
let flushPassiveEffects;
|
||||
let memo;
|
||||
@@ -70,7 +70,7 @@ describe('ReactHooks', () => {
|
||||
useCallback = React.useCallback;
|
||||
useMemo = React.useMemo;
|
||||
useRef = React.useRef;
|
||||
useAPI = React.useAPI;
|
||||
useImperativeMethods = React.useImperativeMethods;
|
||||
forwardRef = React.forwardRef;
|
||||
memo = React.memo;
|
||||
});
|
||||
@@ -87,7 +87,7 @@ describe('ReactHooks', () => {
|
||||
it('resumes after an interruption', () => {
|
||||
function Counter(props, ref) {
|
||||
const [count, updateCount] = useState(0);
|
||||
useAPI(ref, () => ({updateCount}));
|
||||
useImperativeMethods(ref, () => ({updateCount}));
|
||||
return <Text text={props.label + ': ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -171,7 +171,7 @@ describe('ReactHooks', () => {
|
||||
it('simple mount and update', () => {
|
||||
function Counter(props, ref) {
|
||||
const [count, updateCount] = useState(0);
|
||||
useAPI(ref, () => ({updateCount}));
|
||||
useImperativeMethods(ref, () => ({updateCount}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -195,7 +195,7 @@ describe('ReactHooks', () => {
|
||||
ReactNoop.yield('getInitialState');
|
||||
return props.initialState;
|
||||
});
|
||||
useAPI(ref, () => ({updateCount}));
|
||||
useImperativeMethods(ref, () => ({updateCount}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -213,7 +213,7 @@ describe('ReactHooks', () => {
|
||||
function Counter(props, ref) {
|
||||
const [count, updateCount] = useState(0);
|
||||
const [label, updateLabel] = useState('Count');
|
||||
useAPI(ref, () => ({updateCount, updateLabel}));
|
||||
useImperativeMethods(ref, () => ({updateCount, updateLabel}));
|
||||
return <Text text={label + ': ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -232,7 +232,7 @@ describe('ReactHooks', () => {
|
||||
it('callbacks', () => {
|
||||
function Counter(props, ref) {
|
||||
const [count, updateCount] = useState(0);
|
||||
useAPI(ref, () => ({updateCount}));
|
||||
useImperativeMethods(ref, () => ({updateCount}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -264,7 +264,7 @@ describe('ReactHooks', () => {
|
||||
it('does not fire callbacks more than once when rebasing', () => {
|
||||
function Counter(props, ref) {
|
||||
const [count, updateCount] = useState(0);
|
||||
useAPI(ref, () => ({updateCount}));
|
||||
useImperativeMethods(ref, () => ({updateCount}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -507,7 +507,7 @@ describe('ReactHooks', () => {
|
||||
function Counter({row: newRow}, ref) {
|
||||
let [reducer, setReducer] = useState(() => reducerA);
|
||||
let [count, dispatch] = useReducer(reducer, 0);
|
||||
useAPI(ref, () => ({dispatch}));
|
||||
useImperativeMethods(ref, () => ({dispatch}));
|
||||
if (count < 20) {
|
||||
dispatch('increment');
|
||||
// Swap reducers each time we increment
|
||||
@@ -568,7 +568,7 @@ describe('ReactHooks', () => {
|
||||
|
||||
function Counter(props, ref) {
|
||||
const [count, dispatch] = useReducer(reducer, 0);
|
||||
useAPI(ref, () => ({dispatch}));
|
||||
useImperativeMethods(ref, () => ({dispatch}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -609,7 +609,7 @@ describe('ReactHooks', () => {
|
||||
|
||||
function Counter(props, ref) {
|
||||
const [count, dispatch] = useReducer(reducer, 0, initialAction);
|
||||
useAPI(ref, () => ({dispatch}));
|
||||
useImperativeMethods(ref, () => ({dispatch}));
|
||||
return <Text text={'Count: ' + count} />;
|
||||
}
|
||||
Counter = forwardRef(Counter);
|
||||
@@ -910,7 +910,6 @@ describe('ReactHooks', () => {
|
||||
expect(ReactNoop.flush()).toEqual(['Will set count to 1', 'Count: 2']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 2')]);
|
||||
|
||||
flushPassiveEffects();
|
||||
expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);
|
||||
expect(onWorkCanceled).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
@@ -1019,6 +1018,33 @@ describe('ReactHooks', () => {
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
});
|
||||
|
||||
it('unmounts on deletion after skipped effect', () => {
|
||||
function Counter(props) {
|
||||
useEffect(() => {
|
||||
ReactNoop.yield(`Did create [${props.count}]`);
|
||||
return () => {
|
||||
ReactNoop.yield(`Did destroy [${props.count}]`);
|
||||
};
|
||||
}, []);
|
||||
return <Text text={'Count: ' + props.count} />;
|
||||
}
|
||||
ReactNoop.render(<Counter count={0} />);
|
||||
expect(ReactNoop.flush()).toEqual(['Count: 0']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);
|
||||
flushPassiveEffects();
|
||||
expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);
|
||||
|
||||
ReactNoop.render(<Counter count={1} />);
|
||||
expect(ReactNoop.flush()).toEqual(['Count: 1']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
flushPassiveEffects();
|
||||
expect(ReactNoop.clearYields()).toEqual(null);
|
||||
|
||||
ReactNoop.render(null);
|
||||
expect(ReactNoop.flush()).toEqual(['Did destroy [0]']);
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
});
|
||||
|
||||
it('skips effect if constructor has not changed', () => {
|
||||
function effect() {
|
||||
ReactNoop.yield(`Did mount`);
|
||||
|
||||
@@ -29,10 +29,10 @@ import {lazy} from './ReactLazy';
|
||||
import forwardRef from './forwardRef';
|
||||
import memo from './memo';
|
||||
import {
|
||||
useAPI,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeMethods,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useMutationEffect,
|
||||
@@ -89,10 +89,10 @@ if (enableStableConcurrentModeAPIs) {
|
||||
}
|
||||
|
||||
if (enableHooks) {
|
||||
React.useAPI = useAPI;
|
||||
React.useCallback = useCallback;
|
||||
React.useContext = useContext;
|
||||
React.useEffect = useEffect;
|
||||
React.useImperativeMethods = useImperativeMethods;
|
||||
React.useLayoutEffect = useLayoutEffect;
|
||||
React.useMemo = useMemo;
|
||||
React.useMutationEffect = useMutationEffect;
|
||||
|
||||
@@ -110,11 +110,11 @@ export function useMemo(
|
||||
return dispatcher.useMemo(create, inputs);
|
||||
}
|
||||
|
||||
export function useAPI<T>(
|
||||
export function useImperativeMethods<T>(
|
||||
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
|
||||
create: () => T,
|
||||
inputs: Array<mixed> | void | null,
|
||||
): void {
|
||||
const dispatcher = resolveDispatcher();
|
||||
return dispatcher.useAPI(ref, create, inputs);
|
||||
return dispatcher.useImperativeMethods(ref, create, inputs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user