Compare commits
1 Commits
rh/fix-rn-
...
noahlemen/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e3fb8610a |
@@ -175,6 +175,7 @@ describe('ReactDOMServerIntegration', () => {
|
||||
return readContext(Context);
|
||||
}
|
||||
const Memo = React.memo(() => {
|
||||
const [a,b] = React.useState();
|
||||
return readContext(Context);
|
||||
});
|
||||
const FwdRef = React.forwardRef((props, ref) => {
|
||||
|
||||
@@ -59,7 +59,9 @@ describe('ReactDeprecationWarnings', () => {
|
||||
);
|
||||
await expect(async () => await waitForAll([])).toErrorDev(
|
||||
'Warning: FunctionalComponent: Support for defaultProps ' +
|
||||
'will be removed from memo components in a future major ' +
|
||||
`will be removed from ${gate(flags =>
|
||||
flags.enableUserlandMemo ? 'function' : 'memo',
|
||||
)} components in a future major ` +
|
||||
'release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
});
|
||||
|
||||
10
packages/react-is/src/__tests__/ReactIs-test.js
vendored
10
packages/react-is/src/__tests__/ReactIs-test.js
vendored
@@ -160,8 +160,14 @@ describe('ReactIs', () => {
|
||||
const Component = () => React.createElement('div');
|
||||
const Memoized = React.memo(Component);
|
||||
expect(ReactIs.isValidElementType(Memoized)).toBe(true);
|
||||
expect(ReactIs.typeOf(<Memoized />)).toBe(ReactIs.Memo);
|
||||
expect(ReactIs.isMemo(<Memoized />)).toBe(true);
|
||||
expect(ReactIs.typeOf(<Memoized />)).toBe(
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ReactIs.Element : ReactIs.Memo,
|
||||
),
|
||||
);
|
||||
expect(ReactIs.isMemo(<Memoized />)).toBe(
|
||||
gate(flags => !flags.enableUserlandMemo),
|
||||
);
|
||||
expect(ReactIs.isMemo(<Component />)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
11
packages/react-reconciler/src/ReactFiber.js
vendored
11
packages/react-reconciler/src/ReactFiber.js
vendored
@@ -39,6 +39,7 @@ import {
|
||||
enableFloat,
|
||||
enableDO_NOT_USE_disableStrictPassiveEffect,
|
||||
enableRenderableContext,
|
||||
enableUserlandMemo,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {NoFlags, Placement, StaticMask} from './ReactFiberFlags';
|
||||
import {ConcurrentRoot} from './ReactRootTags';
|
||||
@@ -257,7 +258,7 @@ export function resolveLazyComponentTag(Component: Function): WorkTag {
|
||||
if ($$typeof === REACT_FORWARD_REF_TYPE) {
|
||||
return ForwardRef;
|
||||
}
|
||||
if ($$typeof === REACT_MEMO_TYPE) {
|
||||
if (!enableUserlandMemo && $$typeof === REACT_MEMO_TYPE) {
|
||||
return MemoComponent;
|
||||
}
|
||||
}
|
||||
@@ -607,14 +608,16 @@ export function createFiberFromTypeAndProps(
|
||||
resolvedType = resolveForwardRefForHotReloading(resolvedType);
|
||||
}
|
||||
break getTag;
|
||||
case REACT_MEMO_TYPE:
|
||||
fiberTag = MemoComponent;
|
||||
break getTag;
|
||||
case REACT_LAZY_TYPE:
|
||||
fiberTag = LazyComponent;
|
||||
resolvedType = null;
|
||||
break getTag;
|
||||
}
|
||||
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
fiberTag = MemoComponent;
|
||||
break getTag;
|
||||
}
|
||||
}
|
||||
let info = '';
|
||||
if (__DEV__) {
|
||||
|
||||
@@ -111,6 +111,7 @@ import {
|
||||
enablePostpone,
|
||||
enableRenderableContext,
|
||||
enableRefAsProp,
|
||||
enableUserlandMemo,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import isArray from 'shared/isArray';
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
@@ -479,6 +480,10 @@ function updateMemoComponent(
|
||||
nextProps: any,
|
||||
renderLanes: Lanes,
|
||||
): null | Fiber {
|
||||
if (enableUserlandMemo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (current === null) {
|
||||
const type = Component.type;
|
||||
if (
|
||||
@@ -565,6 +570,10 @@ function updateSimpleMemoComponent(
|
||||
nextProps: any,
|
||||
renderLanes: Lanes,
|
||||
): null | Fiber {
|
||||
if (enableUserlandMemo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: current can be non-null here even if the component
|
||||
// hasn't yet mounted. This happens when the inner render suspends.
|
||||
// We'll need to figure out if this is fine or can cause issues.
|
||||
@@ -1694,6 +1703,17 @@ function mountLazyComponent(
|
||||
const resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
|
||||
const resolvedProps = resolveDefaultProps(Component, props);
|
||||
let child;
|
||||
|
||||
if (!enableUserlandMemo && resolvedTag === MemoComponent) {
|
||||
return updateMemoComponent(
|
||||
null,
|
||||
workInProgress,
|
||||
Component,
|
||||
resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
|
||||
renderLanes,
|
||||
);
|
||||
}
|
||||
|
||||
switch (resolvedTag) {
|
||||
case FunctionComponent: {
|
||||
if (__DEV__) {
|
||||
@@ -1738,16 +1758,6 @@ function mountLazyComponent(
|
||||
);
|
||||
return child;
|
||||
}
|
||||
case MemoComponent: {
|
||||
child = updateMemoComponent(
|
||||
null,
|
||||
workInProgress,
|
||||
Component,
|
||||
resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
|
||||
renderLanes,
|
||||
);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
let hint = '';
|
||||
if (__DEV__) {
|
||||
@@ -4038,6 +4048,31 @@ function beginWork(
|
||||
// move this assignment out of the common path and into each branch.
|
||||
workInProgress.lanes = NoLanes;
|
||||
|
||||
if (!enableUserlandMemo) {
|
||||
if (workInProgress.tag === MemoComponent) {
|
||||
const type = workInProgress.type;
|
||||
const unresolvedProps = workInProgress.pendingProps;
|
||||
// Resolve outer props first, then resolve inner props.
|
||||
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
|
||||
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
|
||||
return updateMemoComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
type,
|
||||
resolvedProps,
|
||||
renderLanes,
|
||||
);
|
||||
} else if (workInProgress.tag === SimpleMemoComponent) {
|
||||
return updateSimpleMemoComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
workInProgress.type,
|
||||
workInProgress.pendingProps,
|
||||
renderLanes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
switch (workInProgress.tag) {
|
||||
case IndeterminateComponent: {
|
||||
return mountIndeterminateComponent(
|
||||
@@ -4131,29 +4166,6 @@ function beginWork(
|
||||
return updateContextProvider(current, workInProgress, renderLanes);
|
||||
case ContextConsumer:
|
||||
return updateContextConsumer(current, workInProgress, renderLanes);
|
||||
case MemoComponent: {
|
||||
const type = workInProgress.type;
|
||||
const unresolvedProps = workInProgress.pendingProps;
|
||||
// Resolve outer props first, then resolve inner props.
|
||||
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
|
||||
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
|
||||
return updateMemoComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
type,
|
||||
resolvedProps,
|
||||
renderLanes,
|
||||
);
|
||||
}
|
||||
case SimpleMemoComponent: {
|
||||
return updateSimpleMemoComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
workInProgress.type,
|
||||
workInProgress.pendingProps,
|
||||
renderLanes,
|
||||
);
|
||||
}
|
||||
case IncompleteClassComponent: {
|
||||
const Component = workInProgress.type;
|
||||
const unresolvedProps = workInProgress.pendingProps;
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes';
|
||||
import type {Instance} from './ReactFiberConfig';
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
|
||||
import {enableFloat} from 'shared/ReactFeatureFlags';
|
||||
import {enableFloat, enableUserlandMemo} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
flushSync,
|
||||
scheduleUpdateOnFiber,
|
||||
@@ -181,18 +181,20 @@ export function isCompatibleFamilyForHotReloading(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent: {
|
||||
if ($$typeofNextType === REACT_MEMO_TYPE) {
|
||||
// TODO: if it was but can no longer be simple,
|
||||
// we shouldn't set this.
|
||||
needsCompareFamilies = true;
|
||||
} else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
||||
needsCompareFamilies = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (
|
||||
(!enableUserlandMemo && fiber.tag === MemoComponent) ||
|
||||
fiber.tag === SimpleMemoComponent
|
||||
) {
|
||||
if ($$typeofNextType === REACT_MEMO_TYPE) {
|
||||
// TODO: if it was but can no longer be simple,
|
||||
// we shouldn't set this.
|
||||
needsCompareFamilies = true;
|
||||
} else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
||||
needsCompareFamilies = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -809,7 +809,9 @@ describe('ReactLazy', () => {
|
||||
]
|
||||
: shouldWarnAboutMemoDefaultProps
|
||||
? [
|
||||
'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
`Add: Support for defaultProps will be removed from ${gate(flags =>
|
||||
flags.enableUserlandMemo ? 'function' : 'memo',
|
||||
)} components in a future major release. Use JavaScript default parameters instead.`,
|
||||
]
|
||||
: [],
|
||||
);
|
||||
@@ -1063,7 +1065,11 @@ describe('ReactLazy', () => {
|
||||
await expect(async () => {
|
||||
await act(() => resolveFakeImport(Add));
|
||||
}).toErrorDev(
|
||||
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
`${gate(flags =>
|
||||
flags.enableUserlandMemo ? 'Memo' : 'Unknown',
|
||||
)}: Support for defaultProps will be removed from ${gate(flags =>
|
||||
flags.enableUserlandMemo ? 'function' : 'memo',
|
||||
)} components in a future major release. Use JavaScript default parameters instead.`,
|
||||
);
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
|
||||
@@ -1149,10 +1155,18 @@ describe('ReactLazy', () => {
|
||||
// Mount
|
||||
await expect(async () => {
|
||||
await act(() => resolveFakeImport(Add));
|
||||
}).toErrorDev([
|
||||
'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
]);
|
||||
}).toErrorDev(
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo
|
||||
? [
|
||||
'Memo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
]
|
||||
: [
|
||||
'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
|
||||
// Update
|
||||
|
||||
@@ -444,7 +444,9 @@ describe('memo', () => {
|
||||
});
|
||||
assertLog(['Loading...', 15]);
|
||||
}).toErrorDev([
|
||||
'Counter: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
`Counter: Support for defaultProps will be removed from ${gate(
|
||||
flags => (flags.enableUserlandMemo ? 'function' : 'memo'),
|
||||
)} components in a future major release. Use JavaScript default parameters instead.`,
|
||||
]);
|
||||
expect(ReactNoop).toMatchRenderedOutput(<span prop={15} />);
|
||||
|
||||
@@ -503,7 +505,9 @@ describe('memo', () => {
|
||||
);
|
||||
});
|
||||
}).toErrorDev([
|
||||
'Support for defaultProps will be removed from memo component',
|
||||
`Support for defaultProps will be removed from ${gate(flags =>
|
||||
flags.enableUserlandMemo ? 'function' : 'memo',
|
||||
)} component`,
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput(<div>111</div>);
|
||||
|
||||
@@ -599,6 +603,9 @@ describe('memo', () => {
|
||||
await waitForAll([]);
|
||||
}).toErrorDev(
|
||||
'Each child in a list should have a unique "key" prop. See https://react.dev/link/warning-keys for more information.\n' +
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ' in elementType (at **)\n' : '',
|
||||
) +
|
||||
' in p (at **)',
|
||||
);
|
||||
});
|
||||
@@ -617,6 +624,9 @@ describe('memo', () => {
|
||||
}).toErrorDev(
|
||||
'Each child in a list should have a unique "key" prop. See https://react.dev/link/warning-keys for more information.\n' +
|
||||
' in Inner (at **)\n' +
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ' in elementType (at **)\n' : '',
|
||||
) +
|
||||
' in p (at **)',
|
||||
);
|
||||
});
|
||||
@@ -637,6 +647,9 @@ describe('memo', () => {
|
||||
}).toErrorDev(
|
||||
'Each child in a list should have a unique "key" prop. See https://react.dev/link/warning-keys for more information.\n' +
|
||||
' in Inner (at **)\n' +
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ' in elementType (at **)\n' : '',
|
||||
) +
|
||||
' in p (at **)',
|
||||
);
|
||||
});
|
||||
@@ -656,6 +669,9 @@ describe('memo', () => {
|
||||
}).toErrorDev(
|
||||
'Each child in a list should have a unique "key" prop. See https://react.dev/link/warning-keys for more information.\n' +
|
||||
' in Outer (at **)\n' +
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ' in elementType (at **)\n' : '',
|
||||
) +
|
||||
' in p (at **)',
|
||||
);
|
||||
});
|
||||
@@ -677,6 +693,9 @@ describe('memo', () => {
|
||||
}).toErrorDev(
|
||||
'Each child in a list should have a unique "key" prop. See https://react.dev/link/warning-keys for more information.\n' +
|
||||
' in Inner (at **)\n' +
|
||||
gate(flags =>
|
||||
flags.enableUserlandMemo ? ' in elementType (at **)\n' : '',
|
||||
) +
|
||||
' in p (at **)',
|
||||
);
|
||||
});
|
||||
|
||||
39
packages/react-refresh/src/ReactFreshRuntime.js
vendored
39
packages/react-refresh/src/ReactFreshRuntime.js
vendored
@@ -19,6 +19,8 @@ import type {
|
||||
} from 'react-reconciler/src/ReactFiberHotReloading';
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
|
||||
import {enableUserlandMemo} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {REACT_MEMO_TYPE, REACT_FORWARD_REF_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
type Signature = {
|
||||
@@ -330,13 +332,11 @@ export function register(type: any, id: string): void {
|
||||
|
||||
// Visit inner types because we might not have registered them.
|
||||
if (typeof type === 'object' && type !== null) {
|
||||
switch (getProperty(type, '$$typeof')) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
register(type.render, id + '$render');
|
||||
break;
|
||||
case REACT_MEMO_TYPE:
|
||||
register(type.type, id + '$type');
|
||||
break;
|
||||
const property = getProperty(type, '$$typeof');
|
||||
if (property === REACT_FORWARD_REF_TYPE) {
|
||||
register(type.render, id + '$render');
|
||||
} else if (!enableUserlandMemo && property === REACT_MEMO_TYPE) {
|
||||
register(type.type, id + '$type');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -363,13 +363,11 @@ export function setSignature(
|
||||
}
|
||||
// Visit inner types because we might not have signed them.
|
||||
if (typeof type === 'object' && type !== null) {
|
||||
switch (getProperty(type, '$$typeof')) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
setSignature(type.render, key, forceReset, getCustomHooks);
|
||||
break;
|
||||
case REACT_MEMO_TYPE:
|
||||
setSignature(type.type, key, forceReset, getCustomHooks);
|
||||
break;
|
||||
const property = getProperty(type, '$$typeof');
|
||||
if (property === REACT_FORWARD_REF_TYPE) {
|
||||
setSignature(type.render, key, forceReset, getCustomHooks);
|
||||
} else if (!enableUserlandMemo && property === REACT_MEMO_TYPE) {
|
||||
setSignature(type.type, key, forceReset, getCustomHooks);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -716,14 +714,11 @@ export function isLikelyComponentType(type: any): boolean {
|
||||
}
|
||||
case 'object': {
|
||||
if (type != null) {
|
||||
switch (getProperty(type, '$$typeof')) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
case REACT_MEMO_TYPE:
|
||||
// Definitely React components.
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
const property = getProperty(type, '$$typeof');
|
||||
return (
|
||||
property === REACT_FORWARD_REF_TYPE ||
|
||||
(!enableUserlandMemo && property === REACT_MEMO_TYPE)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
9
packages/react-server/src/ReactFizzServer.js
vendored
9
packages/react-server/src/ReactFizzServer.js
vendored
@@ -146,6 +146,7 @@ import {
|
||||
enablePostpone,
|
||||
enableRenderableContext,
|
||||
enableRefAsProp,
|
||||
enableUserlandMemo,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import assign from 'shared/assign';
|
||||
@@ -1895,15 +1896,15 @@ function renderElement(
|
||||
}
|
||||
|
||||
if (typeof type === 'object' && type !== null) {
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
renderMemo(request, task, keyPath, type, props, ref);
|
||||
return;
|
||||
}
|
||||
switch (type.$$typeof) {
|
||||
case REACT_FORWARD_REF_TYPE: {
|
||||
renderForwardRef(request, task, keyPath, type, props, ref);
|
||||
return;
|
||||
}
|
||||
case REACT_MEMO_TYPE: {
|
||||
renderMemo(request, task, keyPath, type, props, ref);
|
||||
return;
|
||||
}
|
||||
case REACT_PROVIDER_TYPE: {
|
||||
if (!enableRenderableContext) {
|
||||
const context: ReactContext<any> = (type: any)._context;
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
enableServerComponentKeys,
|
||||
enableRefAsProp,
|
||||
enableServerComponentLogs,
|
||||
enableUserlandMemo,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
@@ -823,6 +824,9 @@ function renderElement(
|
||||
// This is a reference to a Client Component.
|
||||
return renderClientElement(task, type, key, props);
|
||||
}
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
return renderElement(request, task, type.type, key, ref, props);
|
||||
}
|
||||
switch (type.$$typeof) {
|
||||
case REACT_LAZY_TYPE: {
|
||||
const payload = type._payload;
|
||||
@@ -833,9 +837,6 @@ function renderElement(
|
||||
case REACT_FORWARD_REF_TYPE: {
|
||||
return renderFunctionComponent(request, task, key, type.render, props);
|
||||
}
|
||||
case REACT_MEMO_TYPE: {
|
||||
return renderElement(request, task, type.type, key, ref, props);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
|
||||
@@ -9,11 +9,17 @@
|
||||
|
||||
import {REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
import {enableUserlandMemo} from 'shared/ReactFeatureFlags';
|
||||
|
||||
export function forwardRef<Props, ElementType: React$ElementType>(
|
||||
render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (render != null && render.$$typeof === REACT_MEMO_TYPE) {
|
||||
if (
|
||||
!enableUserlandMemo &&
|
||||
render != null &&
|
||||
render.$$typeof === REACT_MEMO_TYPE
|
||||
) {
|
||||
console.error(
|
||||
'forwardRef requires a render function but received a `memo` ' +
|
||||
'component. Instead of forwardRef(memo(...)), use ' +
|
||||
|
||||
@@ -7,9 +7,92 @@
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
import {REACT_MEMO_TYPE} from 'shared/ReactSymbols';
|
||||
import type {Dispatcher} from './ReactInternalTypes';
|
||||
|
||||
import {
|
||||
enableUserlandMemo,
|
||||
enableRefAsProp,
|
||||
enableCache,
|
||||
enableUseMemoCacheHook,
|
||||
enableUseEffectEventHook,
|
||||
enableFormActions,
|
||||
enableAsyncActions,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {REACT_ELEMENT_TYPE, REACT_MEMO_TYPE} from 'shared/ReactSymbols';
|
||||
|
||||
import {useState} from './ReactHooks';
|
||||
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
|
||||
import assign from 'shared/assign';
|
||||
|
||||
import isValidElementType from 'shared/isValidElementType';
|
||||
import ReactCurrentDispatcher from './ReactCurrentDispatcher';
|
||||
|
||||
// this currently seems to be duplicated all over the codebase. should it be moved to a shared implementation?
|
||||
function resolveDefaultProps(Component: any, baseProps: Object): Object {
|
||||
if (Component && Component.defaultProps) {
|
||||
// Resolve default props. Taken from ReactElement
|
||||
const props = assign({}, baseProps);
|
||||
const defaultProps = Component.defaultProps;
|
||||
for (const propName in defaultProps) {
|
||||
if (props[propName] === undefined) {
|
||||
props[propName] = defaultProps[propName];
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
return baseProps;
|
||||
}
|
||||
|
||||
function throwInvalidHookError() {
|
||||
throw new Error(
|
||||
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
|
||||
' one of the following reasons:\n' +
|
||||
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
|
||||
'2. You might be breaking the Rules of Hooks\n' +
|
||||
'3. You might have more than one copy of React in the same app\n' +
|
||||
'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
|
||||
);
|
||||
}
|
||||
|
||||
export const MemoCompareDispatcher: Dispatcher = {
|
||||
readContext: throwInvalidHookError,
|
||||
use: throwInvalidHookError,
|
||||
useCallback: throwInvalidHookError,
|
||||
useContext: throwInvalidHookError,
|
||||
useEffect: throwInvalidHookError,
|
||||
useImperativeHandle: throwInvalidHookError,
|
||||
useInsertionEffect: throwInvalidHookError,
|
||||
useLayoutEffect: throwInvalidHookError,
|
||||
useMemo: throwInvalidHookError,
|
||||
useReducer: throwInvalidHookError,
|
||||
useRef: throwInvalidHookError,
|
||||
useState: throwInvalidHookError,
|
||||
useDebugValue: throwInvalidHookError,
|
||||
useDeferredValue: throwInvalidHookError,
|
||||
useTransition: throwInvalidHookError,
|
||||
useSyncExternalStore: throwInvalidHookError,
|
||||
useId: throwInvalidHookError,
|
||||
};
|
||||
if (enableCache) {
|
||||
(MemoCompareDispatcher: Dispatcher).useCacheRefresh = throwInvalidHookError;
|
||||
}
|
||||
if (enableUseMemoCacheHook) {
|
||||
(MemoCompareDispatcher: Dispatcher).useMemoCache = throwInvalidHookError;
|
||||
}
|
||||
if (enableUseEffectEventHook) {
|
||||
(MemoCompareDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError;
|
||||
}
|
||||
if (enableFormActions && enableAsyncActions) {
|
||||
(MemoCompareDispatcher: Dispatcher).useHostTransitionStatus =
|
||||
throwInvalidHookError;
|
||||
(MemoCompareDispatcher: Dispatcher).useFormState = throwInvalidHookError;
|
||||
}
|
||||
if (enableAsyncActions) {
|
||||
(MemoCompareDispatcher: Dispatcher).useOptimistic = throwInvalidHookError;
|
||||
}
|
||||
|
||||
export function memo<Props>(
|
||||
type: React$ElementType,
|
||||
@@ -24,18 +107,78 @@ export function memo<Props>(
|
||||
);
|
||||
}
|
||||
}
|
||||
const elementType = {
|
||||
$$typeof: REACT_MEMO_TYPE,
|
||||
type,
|
||||
compare: compare === undefined ? null : compare,
|
||||
};
|
||||
|
||||
let elementType;
|
||||
if (enableUserlandMemo) {
|
||||
elementType = props => {
|
||||
const resolvedProps = resolveDefaultProps(type, props);
|
||||
const [render, setRender] = useState(() => ({
|
||||
$$typeof: REACT_ELEMENT_TYPE,
|
||||
type,
|
||||
props: resolvedProps,
|
||||
}));
|
||||
|
||||
if (resolvedProps !== render.props) {
|
||||
let previousDispatcher;
|
||||
if (__DEV__) {
|
||||
// previousDispatcher = ReactCurrentDispatcher.current;
|
||||
// ReactCurrentDispatcher.current = MemoCompareDispatcher;
|
||||
}
|
||||
|
||||
let shouldUpdate;
|
||||
try {
|
||||
shouldUpdate =
|
||||
!(compare == null ? shallowEqual : compare)(
|
||||
render.props,
|
||||
resolvedProps,
|
||||
) ||
|
||||
(enableRefAsProp && props.ref !== render.props.ref);
|
||||
} finally {
|
||||
if (__DEV__) {
|
||||
// ReactCurrentDispatcher.current = previousDispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUpdate) {
|
||||
setRender({
|
||||
$$typeof: REACT_ELEMENT_TYPE,
|
||||
type,
|
||||
props: resolvedProps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return render;
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
Object.defineProperty(elementType, 'name', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: function () {
|
||||
return null;
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
elementType = {
|
||||
$$typeof: REACT_MEMO_TYPE,
|
||||
type,
|
||||
compare: compare === undefined ? null : compare,
|
||||
};
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
let ownName;
|
||||
Object.defineProperty(elementType, 'displayName', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
get: function () {
|
||||
return ownName;
|
||||
if (enableUserlandMemo) {
|
||||
return ownName || type.displayName || type.name || 'Memo';
|
||||
} else {
|
||||
return ownName;
|
||||
}
|
||||
},
|
||||
set: function (name) {
|
||||
ownName = name;
|
||||
|
||||
@@ -402,6 +402,7 @@ describe('forwardRef', () => {
|
||||
expect(differentRef.current.type).toBe('div');
|
||||
});
|
||||
|
||||
// @gate !enableUserlandMemo
|
||||
it('warns on forwardRef(memo(...))', () => {
|
||||
expect(() => {
|
||||
React.forwardRef(
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
import type {LazyComponent} from 'react/src/ReactLazy';
|
||||
|
||||
import {enableComponentStackLocations} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
enableComponentStackLocations,
|
||||
enableUserlandMemo,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
REACT_SUSPENSE_TYPE,
|
||||
@@ -368,12 +371,13 @@ export function describeUnknownElementTypeFrameInDEV(
|
||||
return describeBuiltInComponentFrame('SuspenseList', ownerFn);
|
||||
}
|
||||
if (typeof type === 'object') {
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
// Memo may contain any component type so we recursively resolve it.
|
||||
return describeUnknownElementTypeFrameInDEV(type.type, ownerFn);
|
||||
}
|
||||
switch (type.$$typeof) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
return describeFunctionComponentFrame(type.render, ownerFn);
|
||||
case REACT_MEMO_TYPE:
|
||||
// Memo may contain any component type so we recursively resolve it.
|
||||
return describeUnknownElementTypeFrameInDEV(type.type, ownerFn);
|
||||
case REACT_LAZY_TYPE: {
|
||||
const lazyComponent: LazyComponent<any, any> = (type: any);
|
||||
const payload = lazyComponent._payload;
|
||||
|
||||
@@ -284,3 +284,5 @@ export const enableProfilerNestedUpdateScheduledHook = false;
|
||||
export const consoleManagedByDevToolsDuringStrictMode = true;
|
||||
|
||||
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
@@ -20,6 +20,7 @@ import type {LazyComponent} from 'react/src/ReactLazy';
|
||||
|
||||
import isArray from 'shared/isArray';
|
||||
import getPrototypeOf from 'shared/getPrototypeOf';
|
||||
import {enableUserlandMemo} from './ReactFeatureFlags';
|
||||
|
||||
// Used for DEV messages to keep track of which parent rendered some props,
|
||||
// in case they error.
|
||||
@@ -131,11 +132,12 @@ function describeElementType(type: any): string {
|
||||
return 'SuspenseList';
|
||||
}
|
||||
if (typeof type === 'object') {
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
return describeElementType(type.type);
|
||||
}
|
||||
switch (type.$$typeof) {
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
return describeElementType(type.render);
|
||||
case REACT_MEMO_TYPE:
|
||||
return describeElementType(type.type);
|
||||
case REACT_LAZY_TYPE: {
|
||||
const lazyComponent: LazyComponent<any, any> = (type: any);
|
||||
const payload = lazyComponent._payload;
|
||||
|
||||
@@ -108,5 +108,7 @@ export const disableLegacyMode = false;
|
||||
|
||||
export const enableBigIntSupport = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
||||
@@ -99,5 +99,7 @@ export const enableReactTestRendererWarning = false;
|
||||
export const enableBigIntSupport = false;
|
||||
export const disableLegacyMode = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
||||
@@ -90,6 +90,8 @@ export const enableServerComponentKeys = true;
|
||||
export const enableServerComponentLogs = true;
|
||||
export const enableInfiniteRenderLoopDetection = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
// TODO: This must be in sync with the main ReactFeatureFlags file because
|
||||
// the Test Renderer's value must be the same as the one used by the
|
||||
// react package.
|
||||
|
||||
@@ -95,5 +95,7 @@ export const disableLegacyMode = false;
|
||||
|
||||
export const enableBigIntSupport = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
||||
@@ -98,5 +98,7 @@ export const disableLegacyMode = false;
|
||||
|
||||
export const enableBigIntSupport = false;
|
||||
|
||||
export const enableUserlandMemo = false;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|
||||
|
||||
@@ -35,6 +35,7 @@ export const enableRetryLaneExpiration = __VARIANT__;
|
||||
export const retryLaneExpirationMs = 5000;
|
||||
export const syncLaneExpirationMs = 250;
|
||||
export const transitionLaneExpirationMs = 5000;
|
||||
export const enableUserlandMemo = __VARIANT__;
|
||||
|
||||
// Enable this flag to help with concurrent mode debugging.
|
||||
// It logs information to the console about React scheduling, rendering, and commit phases.
|
||||
|
||||
@@ -39,6 +39,7 @@ export const {
|
||||
useModernStrictMode,
|
||||
enableRefAsProp,
|
||||
enableClientRenderFallbackOnTextMismatch,
|
||||
enableUserlandMemo,
|
||||
} = dynamicFeatureFlags;
|
||||
|
||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
enableTransitionTracing,
|
||||
enableCache,
|
||||
enableRenderableContext,
|
||||
enableUserlandMemo,
|
||||
} from './ReactFeatureFlags';
|
||||
|
||||
// Keep in sync with react-reconciler/getComponentNameFromFiber
|
||||
@@ -102,6 +103,15 @@ export default function getComponentNameFromType(type: mixed): string | null {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) {
|
||||
const outerName = (type: any).displayName || null;
|
||||
if (outerName !== null) {
|
||||
return outerName;
|
||||
}
|
||||
return getComponentNameFromType(type.type) || 'Memo';
|
||||
}
|
||||
|
||||
switch (type.$$typeof) {
|
||||
case REACT_PROVIDER_TYPE:
|
||||
if (enableRenderableContext) {
|
||||
@@ -126,12 +136,6 @@ export default function getComponentNameFromType(type: mixed): string | null {
|
||||
}
|
||||
case REACT_FORWARD_REF_TYPE:
|
||||
return getWrappedName(type, type.render, 'ForwardRef');
|
||||
case REACT_MEMO_TYPE:
|
||||
const outerName = (type: any).displayName || null;
|
||||
if (outerName !== null) {
|
||||
return outerName;
|
||||
}
|
||||
return getComponentNameFromType(type.type) || 'Memo';
|
||||
case REACT_LAZY_TYPE: {
|
||||
const lazyComponent: LazyComponent<any, any> = (type: any);
|
||||
const payload = lazyComponent._payload;
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
enableDebugTracing,
|
||||
enableLegacyHidden,
|
||||
enableRenderableContext,
|
||||
enableUserlandMemo,
|
||||
} from './ReactFeatureFlags';
|
||||
|
||||
const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference');
|
||||
@@ -62,7 +63,7 @@ export default function isValidElementType(type: mixed): boolean {
|
||||
if (typeof type === 'object' && type !== null) {
|
||||
if (
|
||||
type.$$typeof === REACT_LAZY_TYPE ||
|
||||
type.$$typeof === REACT_MEMO_TYPE ||
|
||||
(!enableUserlandMemo && type.$$typeof === REACT_MEMO_TYPE) ||
|
||||
type.$$typeof === REACT_CONTEXT_TYPE ||
|
||||
(!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) ||
|
||||
(enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) ||
|
||||
|
||||
Reference in New Issue
Block a user