Compare commits
2 Commits
v19.2.3
...
report-bug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
611f677145 | ||
|
|
7b90545420 |
@@ -7,6 +7,8 @@
|
||||
|
||||
/* eslint-disable react-internal/no-production-logging */
|
||||
|
||||
import {CustomConsole} from '@jest/console';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const util = require('util');
|
||||
const shouldIgnoreConsoleError = require('./shouldIgnoreConsoleError');
|
||||
@@ -62,9 +64,40 @@ const patchConsoleMethod = (methodName, logged) => {
|
||||
|
||||
let logMethod;
|
||||
export function patchConsoleMethods({includeLog} = {includeLog: false}) {
|
||||
// Collapse console logs so they don't log file names.
|
||||
|
||||
// Argument is serialized when passed from jest-cli script through to setupTests.
|
||||
const formatter = (type, message) => {
|
||||
switch (type) {
|
||||
case 'error':
|
||||
return '\x1b[31m' + message + '\x1b[0m';
|
||||
case 'warn':
|
||||
return '\x1b[33m' + message + '\x1b[0m';
|
||||
case 'log':
|
||||
default:
|
||||
return message;
|
||||
}
|
||||
};
|
||||
|
||||
global.console = new CustomConsole(process.stdout, process.stderr, formatter);
|
||||
|
||||
patchConsoleMethod('error', loggedErrors);
|
||||
patchConsoleMethod('warn', loggedWarns);
|
||||
|
||||
const originalLog = console.log;
|
||||
console.log = function (format, ...args) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (typeof args[i] === 'object' && args[i] !== null && args[i].$$typeof) {
|
||||
const render = args[i].render;
|
||||
args[i] = [args[i].$$typeof, 'type', args[i].type];
|
||||
if (render) {
|
||||
args[i].push('render');
|
||||
args[i].push(render);
|
||||
}
|
||||
}
|
||||
}
|
||||
originalLog(format, ...args);
|
||||
};
|
||||
// Only assert console.log isn't called in CI so you can debug tests in DEV.
|
||||
// The matchers will still work in DEV, so you can assert locally.
|
||||
if (includeLog) {
|
||||
|
||||
57
packages/react-reconciler/src/ReactFiber.js
vendored
57
packages/react-reconciler/src/ReactFiber.js
vendored
@@ -327,6 +327,14 @@ export function isFunctionClassComponent(
|
||||
|
||||
// This is used to create an alternate fiber to do work on.
|
||||
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
|
||||
if (current.tag !== HostRoot) {
|
||||
console.group('createWorkInProgress');
|
||||
console.log(' (start)');
|
||||
console.log(' tag ->', current.tag);
|
||||
console.log(' type ->', current.type);
|
||||
console.log(' elementType ->', current.elementType);
|
||||
console.log('');
|
||||
}
|
||||
let workInProgress = current.alternate;
|
||||
if (workInProgress === null) {
|
||||
// We use a double buffering pooling technique because we know that we'll
|
||||
@@ -418,17 +426,34 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (current.tag !== HostRoot) {
|
||||
console.log(' (dev)');
|
||||
console.log(' current');
|
||||
console.log(' tag ->', current?.tag);
|
||||
console.log(' type ->', current?.type);
|
||||
console.log(' elementType ->', current?.elementType);
|
||||
console.log(' wip');
|
||||
console.log(' tag ->', workInProgress.tag);
|
||||
console.log(' type ->', workInProgress.type);
|
||||
console.log(' elementType ->', workInProgress.elementType);
|
||||
}
|
||||
workInProgress._debugInfo = current._debugInfo;
|
||||
workInProgress._debugNeedsRemount = current._debugNeedsRemount;
|
||||
switch (workInProgress.tag) {
|
||||
case FunctionComponent:
|
||||
console.log('');
|
||||
workInProgress.type = resolveFunctionForHotReloading(current.type);
|
||||
// workInProgress.elementType = workInProgress.type;
|
||||
break;
|
||||
case SimpleMemoComponent:
|
||||
console.log('');
|
||||
workInProgress.type = resolveFunctionForHotReloading(current.type);
|
||||
break;
|
||||
case ClassComponent:
|
||||
workInProgress.type = resolveClassForHotReloading(current.type);
|
||||
break;
|
||||
case ForwardRef:
|
||||
console.log('');
|
||||
workInProgress.type = resolveForwardRefForHotReloading(current.type);
|
||||
break;
|
||||
default:
|
||||
@@ -436,6 +461,18 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
|
||||
}
|
||||
}
|
||||
|
||||
if (current.tag !== HostRoot) {
|
||||
console.log(' (end)');
|
||||
console.log(' current');
|
||||
console.log(' tag ->', current?.tag);
|
||||
console.log(' type ->', current?.type);
|
||||
console.log(' elementType ->', current?.elementType);
|
||||
console.log(' wip');
|
||||
console.log(' tag ->', workInProgress.tag);
|
||||
console.log(' type ->', workInProgress.type);
|
||||
console.log(' elementType ->', workInProgress.elementType);
|
||||
}
|
||||
console.groupEnd('createWorkInProgress');
|
||||
return workInProgress;
|
||||
}
|
||||
|
||||
@@ -555,6 +592,11 @@ export function createFiberFromTypeAndProps(
|
||||
mode: TypeOfMode,
|
||||
lanes: Lanes,
|
||||
): Fiber {
|
||||
if (typeof type !== 'string') {
|
||||
console.group('createFiberFromTypeAndProps');
|
||||
console.log(' type ->', type);
|
||||
console.log('');
|
||||
}
|
||||
let fiberTag = FunctionComponent;
|
||||
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
|
||||
let resolvedType = type;
|
||||
@@ -566,7 +608,13 @@ export function createFiberFromTypeAndProps(
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
const ogResolvedType = resolvedType;
|
||||
resolvedType = resolveFunctionForHotReloading(resolvedType);
|
||||
console.log('(early if) createFiberFromTypeAndProps resolved type');
|
||||
console.log(' tag ->', fiberTag);
|
||||
console.log(' before type ->', ogResolvedType);
|
||||
console.log(' resolved type ->', resolvedType);
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
} else if (typeof type === 'string') {
|
||||
@@ -734,6 +782,15 @@ export function createFiberFromTypeAndProps(
|
||||
if (__DEV__) {
|
||||
fiber._debugOwner = owner;
|
||||
}
|
||||
if (typeof type !== 'string') {
|
||||
console.log('(end) createFiberFromTypeAndProps');
|
||||
console.log(' tag ->', fiber.tag);
|
||||
console.log(' og ->', type);
|
||||
console.log(' resolved ->', fiber.type);
|
||||
console.log(' elementType ->', fiber.elementType);
|
||||
console.log('');
|
||||
console.groupEnd('createFiberFromTypeAndProps');
|
||||
}
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ import {
|
||||
resolveFunctionForHotReloading,
|
||||
resolveForwardRefForHotReloading,
|
||||
resolveClassForHotReloading,
|
||||
isCompatibleFamilyForHotReloading,
|
||||
} from './ReactFiberHotReloading';
|
||||
|
||||
import {
|
||||
@@ -485,6 +486,7 @@ function updateMemoComponent(
|
||||
nextProps: any,
|
||||
renderLanes: Lanes,
|
||||
): null | Fiber {
|
||||
console.group('updateMemoComponent');
|
||||
if (current === null) {
|
||||
const type = Component.type;
|
||||
if (
|
||||
@@ -506,6 +508,19 @@ function updateMemoComponent(
|
||||
if (__DEV__) {
|
||||
validateFunctionComponentInDev(workInProgress, type);
|
||||
}
|
||||
console.log(' current');
|
||||
console.log(' needsRemount ->', current?._debugNeedsRemount);
|
||||
console.log(' tag ->', current?.tag);
|
||||
console.log(' type ->', current?.type);
|
||||
console.log(' elementType ->', current?.elementType);
|
||||
console.log(' wip');
|
||||
console.log(' needsRemount ->', workInProgress?._debugNeedsRemount);
|
||||
console.log(' tag ->', workInProgress?.tag);
|
||||
console.log(' type ->', workInProgress?.type);
|
||||
console.log(' elementType ->', workInProgress?.elementType);
|
||||
|
||||
console.log(' return updateSimpleMemoComponent');
|
||||
console.groupEnd('updateMemoComponent');
|
||||
return updateSimpleMemoComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
@@ -537,6 +552,13 @@ function updateMemoComponent(
|
||||
workInProgress.mode,
|
||||
renderLanes,
|
||||
);
|
||||
console.log(' child');
|
||||
console.log(' needsRemount ->', child?._debugNeedsRemount);
|
||||
console.log(' tag ->', child?.tag);
|
||||
console.log(' type ->', child?.type);
|
||||
console.log(' elementType ->', child?.elementType);
|
||||
|
||||
console.groupEnd('updateMemoComponent');
|
||||
child.ref = workInProgress.ref;
|
||||
child.return = workInProgress;
|
||||
workInProgress.child = child;
|
||||
@@ -3819,6 +3841,22 @@ function beginWork(
|
||||
renderLanes: Lanes,
|
||||
): Fiber | null {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
workInProgress?.tag !== HostRoot &&
|
||||
workInProgress.tag !== HostComponent
|
||||
) {
|
||||
console.group('beginWork');
|
||||
console.log(' current');
|
||||
console.log(' needsRemount ->', current?._debugNeedsRemount);
|
||||
console.log(' tag ->', current?.tag);
|
||||
console.log(' type ->', current?.type);
|
||||
console.log(' elementType ->', current?.elementType);
|
||||
console.log(' wip');
|
||||
console.log(' needsRemount ->', workInProgress?._debugNeedsRemount);
|
||||
console.log(' tag ->', workInProgress?.tag);
|
||||
console.log(' type ->', workInProgress?.type);
|
||||
console.log(' elementType ->', workInProgress?.elementType);
|
||||
}
|
||||
if (workInProgress._debugNeedsRemount && current !== null) {
|
||||
// This will restart the begin phase with a new fiber.
|
||||
const copiedFiber = createFiberFromTypeAndProps(
|
||||
@@ -3831,9 +3869,27 @@ function beginWork(
|
||||
);
|
||||
copiedFiber._debugStack = workInProgress._debugStack;
|
||||
copiedFiber._debugTask = workInProgress._debugTask;
|
||||
return remountFiber(current, workInProgress, copiedFiber);
|
||||
console.log('copied fiber');
|
||||
console.log(' original');
|
||||
console.log(' tag -> ', workInProgress.tag);
|
||||
console.log(' type -> ', workInProgress.type);
|
||||
console.log(' elementType -> ', workInProgress.elementType);
|
||||
console.log(' copy');
|
||||
console.log(' tag -> ', copiedFiber.tag);
|
||||
console.log(' type -> ', copiedFiber.type);
|
||||
console.log(' elementType -> ', copiedFiber.elementType);
|
||||
|
||||
const remounted = remountFiber(current, workInProgress, copiedFiber);
|
||||
console.log(' remounted');
|
||||
console.log(' tag -> ', remounted.tag);
|
||||
console.log(' type -> ', remounted.type);
|
||||
console.log(' elementType -> ', remounted.elementType);
|
||||
console.log('');
|
||||
console.groupEnd('beginWork');
|
||||
return remounted;
|
||||
}
|
||||
}
|
||||
console.groupEnd('beginWork');
|
||||
|
||||
if (current !== null) {
|
||||
const oldProps = current.memoizedProps;
|
||||
|
||||
@@ -28,6 +28,8 @@ import {
|
||||
ForwardRef,
|
||||
MemoComponent,
|
||||
SimpleMemoComponent,
|
||||
HostRoot,
|
||||
HostComponent,
|
||||
} from './ReactWorkTags';
|
||||
import {
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
@@ -62,15 +64,40 @@ export const setRefreshHandler = (handler: RefreshHandler | null): void => {
|
||||
};
|
||||
|
||||
export function resolveFunctionForHotReloading(type: any): any {
|
||||
const location = new Error().stack
|
||||
.split('\n')
|
||||
.slice(2, 3)
|
||||
.join('')
|
||||
.replace(/(.*[\\/])/, '')
|
||||
.replace(')', '');
|
||||
if (__DEV__) {
|
||||
if (resolveFamily === null) {
|
||||
console.log(
|
||||
' -> resolveFunctionForHotReloading',
|
||||
location,
|
||||
type,
|
||||
'result -> disabled',
|
||||
);
|
||||
// Hot reloading is disabled.
|
||||
return type;
|
||||
}
|
||||
const family = resolveFamily(type);
|
||||
if (family === undefined) {
|
||||
console.log(
|
||||
' -> resolveFunctionForHotReloading',
|
||||
location,
|
||||
type,
|
||||
'result -> not found',
|
||||
);
|
||||
return type;
|
||||
}
|
||||
|
||||
console.log(
|
||||
' -> resolveFunctionForHotReloading',
|
||||
location,
|
||||
family.current,
|
||||
'result -> new family',
|
||||
);
|
||||
// Use the latest known implementation.
|
||||
return family.current;
|
||||
} else {
|
||||
@@ -84,8 +111,20 @@ export function resolveClassForHotReloading(type: any): any {
|
||||
}
|
||||
|
||||
export function resolveForwardRefForHotReloading(type: any): any {
|
||||
const location = new Error().stack
|
||||
.split('\n')
|
||||
.slice(2, 3)
|
||||
.join('')
|
||||
.replace(/(.*[\\/])/, '')
|
||||
.replace(')', '');
|
||||
if (__DEV__) {
|
||||
if (resolveFamily === null) {
|
||||
console.log(
|
||||
' -> resolveForwardRefForHotReloading',
|
||||
location,
|
||||
type,
|
||||
'result -> disabled',
|
||||
);
|
||||
// Hot reloading is disabled.
|
||||
return type;
|
||||
}
|
||||
@@ -109,11 +148,30 @@ export function resolveForwardRefForHotReloading(type: any): any {
|
||||
if (type.displayName !== undefined) {
|
||||
(syntheticType: any).displayName = type.displayName;
|
||||
}
|
||||
console.log(
|
||||
' -> resolveForwardRefForHotReloading',
|
||||
location,
|
||||
syntheticType,
|
||||
'result -> synthetic',
|
||||
);
|
||||
return syntheticType;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
' -> resolveForwardRefForHotReloading',
|
||||
location,
|
||||
type,
|
||||
'result -> not found',
|
||||
);
|
||||
return type;
|
||||
}
|
||||
console.log(
|
||||
' -> resolveForwardRefForHotReloading',
|
||||
location,
|
||||
type,
|
||||
'result -> new family',
|
||||
);
|
||||
// Use the latest known implementation.
|
||||
return family.current;
|
||||
} else {
|
||||
@@ -134,6 +192,11 @@ export function isCompatibleFamilyForHotReloading(
|
||||
const prevType = fiber.elementType;
|
||||
const nextType = element.type;
|
||||
|
||||
console.log('isCompatibleFamilyForHotReloading');
|
||||
console.log(' type -> ', fiber.type);
|
||||
console.log(' prevType -> ', prevType);
|
||||
console.log(' nextType -> ', nextType);
|
||||
|
||||
// If we got here, we know types aren't === equal.
|
||||
let needsCompareFamilies = false;
|
||||
|
||||
@@ -261,7 +324,7 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
staleFamilies: Set<Family>,
|
||||
): void {
|
||||
if (__DEV__) {
|
||||
const {alternate, child, sibling, tag, type} = fiber;
|
||||
const {alternate, child, sibling, tag, type, elementType} = fiber;
|
||||
|
||||
let candidateType = null;
|
||||
switch (tag) {
|
||||
@@ -283,6 +346,14 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
|
||||
let needsRender = false;
|
||||
let needsRemount = false;
|
||||
const isTypeStale = staleFamilies.has(resolveFamily(type));
|
||||
const isElementTypeStale = staleFamilies.has(resolveFamily(elementType));
|
||||
const isCandidateTypeStale = staleFamilies.has(resolveFamily(elementType));
|
||||
|
||||
if (staleFamilies.has(resolveFamily(elementType))) {
|
||||
needsRemount = true;
|
||||
}
|
||||
|
||||
if (candidateType !== null) {
|
||||
const family = resolveFamily(candidateType);
|
||||
if (family !== undefined) {
|
||||
@@ -307,6 +378,19 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
}
|
||||
}
|
||||
|
||||
if (tag !== HostRoot && tag !== HostComponent) {
|
||||
console.log('scheduleFibersWithFamiliesRecursively');
|
||||
console.log(' type stale ->', isTypeStale);
|
||||
console.log(' candidate stale ->', isCandidateTypeStale);
|
||||
console.log(' element stale ->', isElementTypeStale);
|
||||
console.log(' tag ->', tag);
|
||||
console.log(' candidateType ->', candidateType);
|
||||
console.log(' elementType ->', elementType);
|
||||
console.log(' type ->', type);
|
||||
console.log(' needs render ->', needsRender);
|
||||
console.log(' needs remount ->', needsRemount);
|
||||
}
|
||||
|
||||
if (needsRemount) {
|
||||
fiber._debugNeedsRemount = true;
|
||||
}
|
||||
|
||||
@@ -3103,6 +3103,30 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
|
||||
completedWork,
|
||||
entangledRenderLanes,
|
||||
);
|
||||
if (
|
||||
completedWork.tag !== HostRoot &&
|
||||
completedWork.tag !== HostComponent
|
||||
) {
|
||||
console.log('');
|
||||
console.group('complete fiber');
|
||||
console.log(' current');
|
||||
console.log(' tag ->', current?.tag);
|
||||
console.log(' type ->', current?.type);
|
||||
console.log(' elementType ->', current?.elementType);
|
||||
console.log(' complete');
|
||||
console.log(' tag ->', completedWork?.tag);
|
||||
console.log(' type ->', completedWork?.type);
|
||||
console.log(' elementType ->', completedWork?.elementType);
|
||||
console.log(' next');
|
||||
console.log(' tag ->', next?.tag);
|
||||
console.log(' type ->', next?.type);
|
||||
console.log(' elementType ->', next?.elementType);
|
||||
console.log(' wip');
|
||||
console.log(' tag ->', workInProgress?.tag);
|
||||
console.log(' type ->', workInProgress?.type);
|
||||
console.log(' elementType ->', workInProgress?.elementType);
|
||||
console.groupEnd('complete fiber');
|
||||
}
|
||||
} else {
|
||||
next = completeWork(current, completedWork, entangledRenderLanes);
|
||||
}
|
||||
|
||||
18
packages/react-refresh/src/ReactFreshRuntime.js
vendored
18
packages/react-refresh/src/ReactFreshRuntime.js
vendored
@@ -146,6 +146,12 @@ function canPreserveStateBetween(prevType: any, nextType: any) {
|
||||
if (isReactClass(prevType) || isReactClass(nextType)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof prevType !== typeof nextType ||
|
||||
getProperty(prevType, '$$typeof') !== getProperty(nextType, '$$typeof')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (haveEqualSignatures(prevType, nextType)) {
|
||||
return true;
|
||||
}
|
||||
@@ -198,6 +204,7 @@ export function performReactRefresh(): RefreshUpdate | null {
|
||||
|
||||
isPerformingRefresh = true;
|
||||
try {
|
||||
console.group('React Refresh');
|
||||
const staleFamilies = new Set<Family>();
|
||||
const updatedFamilies = new Set<Family>();
|
||||
|
||||
@@ -213,8 +220,18 @@ export function performReactRefresh(): RefreshUpdate | null {
|
||||
|
||||
// Determine whether this should be a re-render or a re-mount.
|
||||
if (canPreserveStateBetween(prevType, nextType)) {
|
||||
console.log('REFRESH: updated');
|
||||
|
||||
console.log(' prev ->', prevType);
|
||||
console.log(' next ->', nextType);
|
||||
console.log('');
|
||||
updatedFamilies.add(family);
|
||||
} else {
|
||||
console.log('REFRESH: stale');
|
||||
|
||||
console.log(' prev ->', prevType);
|
||||
console.log(' next ->', nextType);
|
||||
console.log('');
|
||||
staleFamilies.add(family);
|
||||
}
|
||||
});
|
||||
@@ -295,6 +312,7 @@ export function performReactRefresh(): RefreshUpdate | null {
|
||||
return update;
|
||||
} finally {
|
||||
isPerformingRefresh = false;
|
||||
console.groupEnd('React Refresh');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -699,6 +699,272 @@ describe('ReactFresh', () => {
|
||||
}
|
||||
});
|
||||
|
||||
fit('can remount when change function to memo', async () => {
|
||||
if (__DEV__) {
|
||||
console.group('\n\n=== initial render\n\n');
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi test');
|
||||
|
||||
console.groupEnd('\n\n=== initial render\n\n');
|
||||
console.group('\n\n=== patch to memo\n\n');
|
||||
// Patch to change function to memo
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check remount
|
||||
expect(container.firstChild !== el).toBe(true);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi memo');
|
||||
|
||||
console.groupEnd('\n\n=== patch to memo\n\n');
|
||||
console.group('\n\n=== patch to original\n\n');
|
||||
|
||||
// Patch back to original function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
console.groupEnd('\n\n=== patch to original\n\n');
|
||||
|
||||
// Check final remount
|
||||
expect(container.firstChild !== nextEl).toBe(true);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test');
|
||||
}
|
||||
});
|
||||
|
||||
fit('can remount when change memo to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
console.group('\n\n=== initial render as memo) \n\n');
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi memo');
|
||||
|
||||
console.groupEnd('\n\n=== initial render as memo) \n\n');
|
||||
console.group('\n\n=== patch to fowardRef\n\n');
|
||||
// Patch to change memo to forwardRef
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi forwardRef</p>;
|
||||
}
|
||||
const Test = React.forwardRef(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
// Check remount
|
||||
expect(container.firstChild !== el).toBe(true);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
console.groupEnd('\n\n=== patch to fowardRef\n\n');
|
||||
console.group('\n\n=== patch back to memo\n\n');
|
||||
// Patch back to memo
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi memo</p>;
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
console.groupEnd('\n\n=== patch back to memo\n\n');
|
||||
// Check final remount
|
||||
expect(container.firstChild !== nextEl).toBe(true);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi memo');
|
||||
}
|
||||
});
|
||||
|
||||
fit('can remount when change function to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
console.group('\n\n=== initial render\n\n');
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check the initial render
|
||||
const el = container.firstChild;
|
||||
expect(el.textContent).toBe('hi test');
|
||||
|
||||
console.groupEnd('\n\n=== initial render\n\n');
|
||||
console.group('\n\n=== patch to forwardRef \n\n');
|
||||
// Patch to change function to forwardRef
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
return <p>hi forwardRef</p>;
|
||||
}
|
||||
const Test = React.forwardRef(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check remount
|
||||
expect(container.firstChild !== el).toBe(true);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
console.groupEnd('\n\n=== patch to forwardRef \n\n');
|
||||
console.group('\n\n=== patch to original \n\n');
|
||||
// Patch back to a new function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test1</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
console.groupEnd('\n\n=== patch to original \n\n');
|
||||
// Check final remount
|
||||
expect(container.firstChild !== nextEl).toBe(true);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test1');
|
||||
}
|
||||
});
|
||||
|
||||
fit('resets state when switching between different component types', async () => {
|
||||
if (__DEV__) {
|
||||
console.group('\n\n=== initial render \n\n');
|
||||
await act(async () => {
|
||||
await render(() => {
|
||||
function Test() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
return (
|
||||
<div onClick={() => setCount(c => c + 1)}>count: {count}</div>
|
||||
);
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
|
||||
console.groupEnd('\n\n=== initial render \n\n');
|
||||
console.group('\n\n=== patch to memo \n\n');
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test2() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
return (
|
||||
<div onClick={() => setCount(c => c + 1)}>count: {count}</div>
|
||||
);
|
||||
}
|
||||
const Test = React.memo(Test2);
|
||||
$RefreshReg$(Test2, 'Test2');
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
console.groupEnd('\n\n=== patch to memo \n\n');
|
||||
console.group('\n\n=== click with memo \n\n');
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
|
||||
console.groupEnd('\n\n=== click with memo \n\n');
|
||||
console.group('\n\n=== patch to forwardRed \n\n');
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
const Test = React.forwardRef((props, ref) => {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const handleClick = () => setCount(c => c + 1);
|
||||
|
||||
// Ensure ref is extensible
|
||||
const divRef = React.useRef(null);
|
||||
React.useEffect(() => {
|
||||
if (ref) {
|
||||
if (typeof ref === 'function') {
|
||||
ref(divRef.current);
|
||||
} else if (Object.isExtensible(ref)) {
|
||||
ref.current = divRef.current;
|
||||
}
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
return (
|
||||
<div ref={divRef} onClick={handleClick}>
|
||||
count: {count}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
console.groupEnd('\n\n=== patch to forwardRed \n\n');
|
||||
console.group('\n\n=== click with forwardRed \n\n');
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
console.groupEnd('\n\n=== click with forwardRed \n\n');
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
}
|
||||
});
|
||||
|
||||
it('can update simple memo function in isolation', async () => {
|
||||
if (__DEV__) {
|
||||
await render(() => {
|
||||
|
||||
Reference in New Issue
Block a user