'use strict';
describe('ReactStartTransitionMultipleRenderers', () => {
let act;
let container;
let React;
let ReactDOMClient;
let Scheduler;
let assertLog;
let startTransition;
let useOptimistic;
let textCache;
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOMClient = require('react-dom/client');
Scheduler = require('scheduler');
act = require('internal-test-utils').act;
assertLog = require('internal-test-utils').assertLog;
startTransition = React.startTransition;
useOptimistic = React.useOptimistic;
container = document.createElement('div');
document.body.appendChild(container);
textCache = new Map();
});
function resolveText(text) {
const record = textCache.get(text);
if (record === undefined) {
const newRecord = {
status: 'resolved',
value: text,
};
textCache.set(text, newRecord);
} else if (record.status === 'pending') {
const thenable = record.value;
record.status = 'resolved';
record.value = text;
thenable.pings.forEach(t => t(text));
}
}
function getText(text) {
const record = textCache.get(text);
if (record === undefined) {
const thenable = {
pings: [],
then(resolve) {
if (newRecord.status === 'pending') {
thenable.pings.push(resolve);
} else {
Promise.resolve().then(() => resolve(newRecord.value));
}
},
};
const newRecord = {
status: 'pending',
value: thenable,
};
textCache.set(text, newRecord);
return thenable;
} else {
switch (record.status) {
case 'pending':
return record.value;
case 'rejected':
return Promise.reject(record.value);
case 'resolved':
return Promise.resolve(record.value);
}
}
}
function Text({text}) {
Scheduler.log(text);
return text;
}
afterEach(() => {
document.body.removeChild(container);
});
it('React.startTransition works across multiple renderers', async () => {
const ReactNoop = require('react-noop-renderer');
const setIsPendings = new Set();
function App() {
const [isPending, setIsPending] = useOptimistic(false);
setIsPendings.add(setIsPending);
return <Text text={isPending ? 'Pending' : 'Not pending'} />;
}
const noopRoot = ReactNoop.createRoot(null);
const domRoot = ReactDOMClient.createRoot(container);
await act(() => {
noopRoot.render(<App />);
domRoot.render(<App />);
});
assertLog(['Not pending', 'Not pending']);
expect(container.textContent).toEqual('Not pending');
expect(noopRoot).toMatchRenderedOutput('Not pending');
await act(() => {
startTransition(async () => {
await getText('Wait before setting isPending');
setIsPendings.forEach(setIsPending => setIsPending(true));
await getText('Wait until end of async action');
});
});
await act(() => resolveText('Wait before setting isPending'));
assertLog(['Pending', 'Pending']);
expect(container.textContent).toEqual('Pending');
expect(noopRoot).toMatchRenderedOutput('Pending');
await act(() => resolveText('Wait until end of async action'));
assertLog(['Not pending', 'Not pending']);
expect(container.textContent).toEqual('Not pending');
expect(noopRoot).toMatchRenderedOutput('Not pending');
});
});