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
38 changes: 12 additions & 26 deletions packages/react-reconciler/src/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,7 @@ import {
} from './ReactWorkTags';
import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber';
import {isDevToolsPresent} from './ReactFiberDevToolsHook';
import {
resolveClassForHotReloading,
resolveFunctionForHotReloading,
resolveForwardRefForHotReloading,
} from './ReactFiberHotReloading';
import {resolveTypeForHotReloading} from './ReactFiberHotReloading';
import {NoLanes} from './ReactFiberLane';
import {
NoMode,
Expand Down Expand Up @@ -427,13 +423,10 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
switch (workInProgress.tag) {
case FunctionComponent:
case SimpleMemoComponent:
workInProgress.type = resolveFunctionForHotReloading(current.type);
break;
case MemoComponent:
case ClassComponent:
workInProgress.type = resolveClassForHotReloading(current.type);
break;
case ForwardRef:
workInProgress.type = resolveForwardRefForHotReloading(current.type);
workInProgress.type = resolveTypeForHotReloading(current.type);
break;
default:
break;
Expand Down Expand Up @@ -569,18 +562,14 @@ export function createFiberFromTypeAndProps(
let fiberTag: WorkTag = FunctionComponent;
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
let resolvedType = type;
if (typeof type === 'function') {
if (shouldConstruct(type)) {
if (__DEV__) {
resolvedType = resolveTypeForHotReloading(type);
}
if (typeof resolvedType === 'function') {
if (shouldConstruct(resolvedType)) {
fiberTag = ClassComponent;
if (__DEV__) {
resolvedType = resolveClassForHotReloading(resolvedType);
}
} else {
if (__DEV__) {
resolvedType = resolveFunctionForHotReloading(resolvedType);
}
}
} else if (typeof type === 'string') {
} else if (typeof resolvedType === 'string') {
// $FlowFixMe[constant-condition]
if (supportsResources && supportsSingletons) {
const hostContext = getHostContext();
Expand All @@ -602,7 +591,7 @@ export function createFiberFromTypeAndProps(
fiberTag = HostComponent;
}
} else {
getTag: switch (type) {
getTag: switch (resolvedType) {
// $FlowFixMe[invalid-compare]
case REACT_ACTIVITY_TYPE:
return createFiberFromActivity(pendingProps, mode, lanes, key);
Expand Down Expand Up @@ -650,8 +639,8 @@ export function createFiberFromTypeAndProps(
// Fall through
default: {
// $FlowFixMe[invalid-compare]
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
if (typeof resolvedType === 'object' && resolvedType !== null) {
switch (resolvedType.$$typeof) {
// $FlowFixMe[invalid-compare]
case REACT_CONTEXT_TYPE:
fiberTag = ContextProvider;
Expand All @@ -664,9 +653,6 @@ export function createFiberFromTypeAndProps(
// $FlowFixMe[invalid-compare]
case REACT_FORWARD_REF_TYPE:
fiberTag = ForwardRef;
if (__DEV__) {
resolvedType = resolveForwardRefForHotReloading(resolvedType);
}
break getTag;
// $FlowFixMe[invalid-compare]
case REACT_MEMO_TYPE:
Expand Down
40 changes: 18 additions & 22 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,7 @@ import {
REACT_CONTEXT_TYPE,
} from 'shared/ReactSymbols';
import {setCurrentFiber} from './ReactCurrentFiber';
import {
resolveFunctionForHotReloading,
resolveForwardRefForHotReloading,
resolveClassForHotReloading,
resolveRemountTypeForHotReloading,
} from './ReactFiberHotReloading';
import {resolveTypeForHotReloading} from './ReactFiberHotReloading';

import {
mountChildFibers,
Expand Down Expand Up @@ -414,7 +409,16 @@ function updateForwardRef(
// TODO: current can be non-null here even if the component
// hasn't yet mounted. This happens after the first render suspends.
// We'll need to figure out if this is fine or can cause issues.
const render = Component.render;
let render = Component.render;
if (__DEV__) {
const resolvedRender = resolveTypeForHotReloading(render);
if (resolvedRender !== render) {
render = resolvedRender;
if (current !== null) {
didReceiveUpdate = true;
}
}
}
const ref = workInProgress.ref;

let propsWithoutRef;
Expand Down Expand Up @@ -482,7 +486,7 @@ function updateMemoComponent(
if (isSimpleFunctionComponent(type) && Component.compare === null) {
let resolvedType = type;
if (__DEV__) {
resolvedType = resolveFunctionForHotReloading(type);
resolvedType = resolveTypeForHotReloading(type);
}
// If this is a plain function component without default props,
// and with only the default shallow comparison, we upgrade it
Expand Down Expand Up @@ -2095,17 +2099,16 @@ function mountLazyComponent(
const props = workInProgress.pendingProps;
const lazyComponent: LazyComponentType<any, any> = elementType;
let Component = resolveLazy(lazyComponent);
if (__DEV__) {
Component = resolveTypeForHotReloading(Component);
}
// Store the unwrapped component in the type.
workInProgress.type = Component;

if (typeof Component === 'function') {
if (isFunctionClassComponent(Component)) {
const resolvedProps = resolveClassComponentProps(Component, props);
workInProgress.tag = ClassComponent;
if (__DEV__) {
workInProgress.type = Component =
resolveClassForHotReloading(Component);
}
return updateClassComponent(
null,
workInProgress,
Expand All @@ -2117,8 +2120,6 @@ function mountLazyComponent(
workInProgress.tag = FunctionComponent;
if (__DEV__) {
validateFunctionComponentInDev(workInProgress, Component);
workInProgress.type = Component =
resolveFunctionForHotReloading(Component);
}
return updateFunctionComponent(
null,
Expand All @@ -2134,10 +2135,6 @@ function mountLazyComponent(
// $FlowFixMe[invalid-compare]
if ($$typeof === REACT_FORWARD_REF_TYPE) {
workInProgress.tag = ForwardRef;
if (__DEV__) {
workInProgress.type = Component =
resolveForwardRefForHotReloading(Component);
}
return updateForwardRef(
null,
workInProgress,
Expand Down Expand Up @@ -4196,10 +4193,9 @@ function beginWork(
if (workInProgress._debugNeedsRemount && current !== null) {
// This will restart the begin phase with a new fiber.
const copiedFiber = createFiberFromTypeAndProps(
resolveRemountTypeForHotReloading(
workInProgress.elementType,
workInProgress.type,
),
// Remount from the fiber's outermost identity; mounting resolves
// any inner types to their latest implementations.
resolveTypeForHotReloading(workInProgress.elementType),
workInProgress.key,
workInProgress.pendingProps,
workInProgress._debugOwner || null,
Expand Down
81 changes: 12 additions & 69 deletions packages/react-reconciler/src/ReactFiberHotReloading.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const setRefreshHandler = (handler: RefreshHandler | null): void => {
}
};

export function resolveFunctionForHotReloading(type: any): any {
export function resolveTypeForHotReloading(type: any): any {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
Expand All @@ -78,72 +78,6 @@ export function resolveFunctionForHotReloading(type: any): any {
}
}

export function resolveClassForHotReloading(type: any): any {
// No implementation differences.
return resolveFunctionForHotReloading(type);
}

export function resolveForwardRefForHotReloading(type: any): any {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
return type;
}
const family = resolveFamily(type);
if (family === undefined) {
// Check if we're dealing with a real forwardRef. Don't want to crash early.
if (
type !== null &&
type !== undefined &&
typeof type.render === 'function'
) {
// ForwardRef is special because its resolved .type is an object,
// but it's possible that we only have its inner render function in the map.
// If that inner render function is different, we'll build a new forwardRef type.
const currentRender = resolveFunctionForHotReloading(type.render);
if (type.render !== currentRender) {
const syntheticType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render: currentRender,
};
if (type.displayName !== undefined) {
(syntheticType as any).displayName = type.displayName;
}
return syntheticType;
}
}
return type;
}
// Use the latest known implementation.
return family.current;
} else {
return type;
}
}

export function resolveRemountTypeForHotReloading(
elementType: any,
type: any,
): any {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
return type;
}
// The elementType is the fiber's public identity, so its family tracks
// the latest implementation even when an edit changed the kind of the
// type (e.g. memo to a plain function) and `type` still points at the
// old inner implementation.
const family = resolveFamily(elementType);
if (family === undefined) {
return type;
}
return family.current;
} else {
return type;
}
}

export function isCompatibleFamilyForHotReloading(
fiber: Fiber,
element: ReactElement,
Expand Down Expand Up @@ -196,8 +130,6 @@ export function isCompatibleFamilyForHotReloading(
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;
Expand Down Expand Up @@ -339,6 +271,17 @@ function scheduleFibersWithFamiliesRecursively(
const outerFamily = resolve(outerCandidateType);
if (outerFamily !== undefined && staleFamilies.has(outerFamily)) {
needsRemount = true;
} else if (
typeof outerCandidateType === 'object' &&
outerCandidateType.$$typeof === REACT_LAZY_TYPE
) {
const payload = outerCandidateType._payload;
if (payload._status === 1 /* Resolved; see ReactLazy */) {
const middleFamily = resolve(payload._result.default);
if (middleFamily !== undefined && staleFamilies.has(middleFamily)) {
needsRemount = true;
}
}
}
}
if (failedBoundaries !== null) {
Expand Down
12 changes: 11 additions & 1 deletion packages/react-refresh/src/ReactFreshRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ function canPreserveStateBetween(prevType: any, nextType: any) {
) {
return false;
}
// Switching from SimpleMemoComponent to MemoComponent requires a remount;
// for symmetry, remount for the reverse too.
if (getProperty(prevType, '$$typeof') === REACT_MEMO_TYPE) {
if (
(getProperty(prevType, 'compare') === null) !==
(getProperty(nextType, 'compare') === null)
) {
return false;
}
}
}
if (haveEqualSignatures(prevType, nextType)) {
return true;
Expand Down Expand Up @@ -187,7 +197,7 @@ function cloneSet<T>(set: Set<T>): Set<T> {
}

// This is a safety mechanism to protect against rogue getters and Proxies.
function getProperty(object: any, property: string) {
function getProperty(object: any, property: string): any {
try {
return object[property];
} catch (err) {
Expand Down
Loading
Loading