[Blocks] Fix stale data on updates (#18810)

* [Blocks] Failing test for nested load

* Simplify the test

* Add a similar test that fails in PROD

* Copy .type when cloning work in progress
This commit is contained in:
Dan Abramov
2020-05-04 01:43:51 +01:00
committed by GitHub
parent 64d4b84204
commit d830cd9984
3 changed files with 91 additions and 0 deletions
@@ -282,6 +282,8 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;
// Needed because Blocks store data on type.
workInProgress.type = current.type;
// We already have an alternate.
// Reset the effect tag.
@@ -415,6 +417,8 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
// Needed because Blocks store data on type.
workInProgress.type = current.type;
// Clone the dependencies object. This is mutated during the render phase, so
// it cannot be shared with the current fiber.
@@ -277,6 +277,8 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;
// Needed because Blocks store data on type.
workInProgress.type = current.type;
// We already have an alternate.
// Reset the effect tag.
@@ -413,6 +415,8 @@ export function resetWorkInProgress(
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
// Needed because Blocks store data on type.
workInProgress.type = current.type;
// Clone the dependencies object. This is mutated during the render phase, so
// it cannot be shared with the current fiber.
@@ -257,4 +257,87 @@ describe('ReactBlocks', () => {
</>,
);
});
// Regression test.
// @gate experimental
it('does not render stale data after ping', async () => {
function Child() {
return <span>Name: {readString('Sebastian')}</span>;
}
const loadParent = block(
function Parent(props, data) {
return (
<Suspense fallback="Loading...">
{data.name ? <Child /> : <span>Empty</span>}
</Suspense>
);
},
function load(name) {
return {name};
},
);
function App({Page}) {
return <Page />;
}
await ReactNoop.act(async () => {
ReactNoop.render(<App Page={loadParent(null)} />);
});
expect(ReactNoop).toMatchRenderedOutput(<span>Empty</span>);
await ReactNoop.act(async () => {
ReactNoop.render(<App Page={loadParent('Sebastian')} />);
});
await ReactNoop.act(async () => {
jest.advanceTimersByTime(1000);
});
expect(ReactNoop).toMatchRenderedOutput(<span>Name: Sebastian</span>);
});
// Regression test.
// @gate experimental
it('does not render stale data after ping and setState', async () => {
function Child() {
return <span>Name: {readString('Sebastian')}</span>;
}
let _setSuspend;
const loadParent = block(
function Parent(props, data) {
const [suspend, setSuspend] = useState(true);
_setSuspend = setSuspend;
if (!suspend) {
return <span>{data.name}</span>;
}
return (
<Suspense fallback="Loading...">
{data.name ? <Child /> : <span>Empty</span>}
</Suspense>
);
},
function load(name) {
return {name};
},
);
function App({Page}) {
return <Page />;
}
await ReactNoop.act(async () => {
ReactNoop.render(<App Page={loadParent(null)} />);
});
expect(ReactNoop).toMatchRenderedOutput(<span>Empty</span>);
await ReactNoop.act(async () => {
ReactNoop.render(<App Page={loadParent('Sebastian')} />);
});
await ReactNoop.act(async () => {
_setSuspend(false);
jest.advanceTimersByTime(1000);
});
expect(ReactNoop).toMatchRenderedOutput(<span>Sebastian</span>);
});
});