Compare commits
1 Commits
sapling-pr
...
repro-bug-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b90545420 |
@@ -261,7 +261,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 +283,11 @@ function scheduleFibersWithFamiliesRecursively(
|
||||
|
||||
let needsRender = false;
|
||||
let needsRemount = false;
|
||||
if (staleFamilies.has(resolveFamily(elementType))) {
|
||||
console.log('gonna mark stale fiber for remount');
|
||||
needsRemount = true;
|
||||
}
|
||||
|
||||
if (candidateType !== null) {
|
||||
const family = resolveFamily(candidateType);
|
||||
if (family !== undefined) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -215,6 +221,7 @@ export function performReactRefresh(): RefreshUpdate | null {
|
||||
if (canPreserveStateBetween(prevType, nextType)) {
|
||||
updatedFamilies.add(family);
|
||||
} else {
|
||||
console.log('mark family as stale', family);
|
||||
staleFamilies.add(family);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -699,6 +699,244 @@ describe('ReactFresh', () => {
|
||||
}
|
||||
});
|
||||
|
||||
fit('can remount when change function to memo', async () => {
|
||||
if (__DEV__) {
|
||||
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');
|
||||
|
||||
// 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.log('patch to original');
|
||||
|
||||
// Patch back to original function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check final remount
|
||||
expect(container.firstChild !== nextEl).toBe(true);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test');
|
||||
}
|
||||
});
|
||||
|
||||
it('can remount when change memo to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
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');
|
||||
|
||||
// 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).not.toBe(el);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
// 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;
|
||||
});
|
||||
});
|
||||
// Check final remount
|
||||
expect(container.firstChild).not.toBe(nextEl);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi memo');
|
||||
}
|
||||
});
|
||||
|
||||
it('can remount when change function to forwardRef', async () => {
|
||||
if (__DEV__) {
|
||||
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');
|
||||
|
||||
// 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).not.toBe(el);
|
||||
const nextEl = container.firstChild;
|
||||
expect(nextEl.textContent).toBe('hi forwardRef');
|
||||
|
||||
// Patch back to a new function
|
||||
await act(async () => {
|
||||
await patch(() => {
|
||||
function Test() {
|
||||
return <p>hi test1</p>;
|
||||
}
|
||||
$RefreshReg$(Test, 'Test');
|
||||
return Test;
|
||||
});
|
||||
});
|
||||
|
||||
// Check final remount
|
||||
expect(container.firstChild).not.toBe(nextEl);
|
||||
const newEl = container.firstChild;
|
||||
expect(newEl.textContent).toBe('hi test1');
|
||||
}
|
||||
});
|
||||
|
||||
it('resets state when switching between different component types', async () => {
|
||||
if (__DEV__) {
|
||||
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');
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
expect(container.firstChild.textContent).toBe('count: 1');
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
expect(container.firstChild.textContent).toBe('count: 0');
|
||||
await act(async () => {
|
||||
container.firstChild.click();
|
||||
});
|
||||
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