fix: Upload in React 18 sync problem (#41082)

* fix: Upload in React 18 sync problem

* test: add test case
This commit is contained in:
lijianan 2023-03-06 16:38:46 +08:00 committed by GitHub
parent 55166d4272
commit c38108a63c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 2 deletions

View File

@ -116,7 +116,9 @@ const InternalUpload: React.ForwardRefRenderFunction<unknown, UploadProps> = (pr
changeInfo.event = event;
}
onChange?.(changeInfo);
flushSync(() => {
onChange?.(changeInfo);
});
};
const mergedBeforeUpload = async (file: RcFile, fileListArgs: RcFile[]) => {

View File

@ -7,7 +7,7 @@ import type { RcFile, UploadFile, UploadProps } from '..';
import Upload from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, waitFakeTimer, act } from '../../../tests/utils';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import Form from '../../form';
import { resetWarned } from '../../_util/warning';
import { getFileItem, isImageUrl, removeFileItem } from '../utils';
@ -946,4 +946,51 @@ describe('Upload', () => {
}),
);
});
it('prevent auto batch in control mode', async () => {
const mockFile1 = new File(['bamboo'], 'bamboo.png', { type: 'image/png' });
const mockFile2 = new File(['light'], 'light.png', { type: 'image/png' });
const customRequest = jest.fn(async (options) => {
// stop here to make sure new fileList has been set and passed to Upload
// eslint-disable-next-line no-promise-executor-return
await new Promise((resolve) => setTimeout(resolve, 0));
options.onProgress({ percent: 0 });
const url = Promise.resolve<string>('https://ant.design');
options.onProgress({ percent: 100 });
options.onSuccess({}, { ...options.file, url });
});
let fileListOut: UploadProps['fileList'] = [];
const Demo: React.FC = () => {
const [fileList, setFileList] = React.useState<UploadFile[]>([]);
const onChange: UploadProps['onChange'] = async (e) => {
const newFileList = Array.isArray(e) ? e : e.fileList;
setFileList(newFileList);
fileListOut = newFileList;
};
return (
<Upload customRequest={customRequest} onChange={onChange} fileList={fileList}>
<button type="button">Upload</button>
</Upload>
);
};
const { container } = render(<Demo />);
fireEvent.change(container.querySelector<HTMLInputElement>('input')!, {
target: { files: [mockFile1, mockFile2] },
});
// React 18 is async now
await waitFakeTimer();
fileListOut.forEach((file) => {
expect(file.status).toBe('done');
});
});
});