react-google-recaptcha-v3 icon indicating copy to clipboard operation
react-google-recaptcha-v3 copied to clipboard

Nextjs upload bug

Open fred-boink opened this issue 3 years ago • 3 comments

When using Nextjs and uploading lots of content, recaptcha seems to be added multiple times to DOM and eventually crashes the app.

fred-boink avatar Aug 28 '21 15:08 fred-boink

Hi @fred-boink, without an example of your code I will not be able to assist or tell what's wrong.

t49tran avatar Aug 29 '21 05:08 t49tran

Hi @t49tran

in my app.tsx

<GoogleReCaptchaProvider 
	scriptProps={{ async: true, defer: true, appendTo: 'body' }} 
	reCaptchaKey={process.env.NEXT_PUBLIC_GOOGLE_CAPTCHA_KEY} 
	useEnterprise 
	> 
	<ToastProvider
				placement="top-center"
				components={{ Toast: Toaster }}
				autoDismiss
			>
				<IntercomProvider
					appId={process.env.NEXT_PUBLIC_INTERCOM_APP_ID}
				>
					<LoginOverlayProvider>
						<UnsupportedBrowserGuard>
							<DesktopGuard>
							<AuthProvider>
								<Component {...pageProps} />
							</AuthProvider>
						</DesktopGuard>
						</UnsupportedBrowserGuard>
					</LoginOverlayProvider>
				</IntercomProvider>
			</ToastProvider>
		</GoogleReCaptchaProvider> 

Then we have inputs for uploading files in another component

const UploadPostButton = ({ postDraft, saveDraft }) => {
	const fileUpload = useRef<HTMLInputElement>(null);
	const { uploadFile } = useUploadStore();

	const onFileSelected = async (
		fileUploadEvent: ChangeEvent<HTMLInputElement>
	) => {
		const { files } = fileUploadEvent.target;

		for (let i = 0; i < files.length; i++) {
			const file: File = files[i];
			let postItem: DraftPostItemType;
			let fileToUpload;

			if (file.type.split('/')[0] === PostItemTypes.VIDEO) {
				const isMultiPart = IsMultiPart(file);
				const { vaultId, vaultVideoPresignedUrl, key, uploadId } =
					await getVideoSignedUrl(file.name, isMultiPart);
				fileToUpload = {
					file,
					presignedUrl: vaultVideoPresignedUrl,
					key,
					uploadId,
					vaultId
				};
				postItem = new VideoDraftPostItem({
					vaultId,
					status: PostItemStatuses.LOCAL,
					uploadedFileName: file.name
				});
				await postItem.setEnd(file);
				await postItem.setLocalThumbnail(file);
			} else {
				const { vaultId, vaultImagePresignedUrl } =
					await getImageSignedUrl(file.name);

				fileToUpload = {
					file,
					presignedUrl: vaultImagePresignedUrl,
					vaultId
				};
				postItem = new ImageDraftPostItem({
					vaultId,
					status: PostItemStatuses.LOCAL,
					uploadedFileName: file.name
				});
				await postItem.cropImageFromFile(file);
			}
			await postItem.setMediaUrlFromFile(file);

			// if no post cover set
			// first post item as post cover
			if (!postDraft.postCover || postDraft.postItems.length === 0) {
				postDraft.postCover = postItem;
			}

			postDraft.postItems = postDraft.postItems.concat(postItem);
			uploadFile(fileToUpload);
		}

		await saveDraft(postDraft);
	};

	return (
		<>
			<div
				className={classNames(
					'flex',
					'items-center',
					'justify-center',
					// 'py-15',
					// 'flex-1/4',
					'w-full',
					'bg-white',
					'bg-opacity-5'
				)}
			>
				<div
					className={classNames(
						'flex',
						'justify-center',
						'py-40',
						'pwa:pb-safe-15',
						'space-x-20'
					)}
				>
					<div>
						<input
							type="file"
							// heic must be explicitely added
							accept="image/*,video/*,image/heic"
							ref={fileUpload}
							hidden
							multiple
							onChange={(e) => onFileSelected(e)}
						/>
						<button
							type="button"
							onClick={() => fileUpload.current.click()}
							className={classNames(
								'flex',
								'items-center',
								'flex-col'
							)}
						>
							<div
								className={classNames(
									'flex',
									'items-center',
									'justify-center',
									'rounded-full',
									'wh-80',
									'bg-blue-700'
								)}
							>
								<Add
									className={classNames(
										'wh-32',
										'text-gray-700'
									)}
								/>
							</div>
							{/* <p
								className={classNames(
									'mt-8',
									'text-12',
									'text-blue-700',
									'text-center'
								)}
							>
								Add
							</p> */}
						</button>
					</div>
					{/* <Link href="/posts?status=draft">
						<a
							className={classNames(
								'flex',
								'items-center',
								'flex-col'
							)}
						>
							<div
								className={classNames(
									'flex',
									'items-center',
									'justify-center',
									'rounded-full',
									'wh-80',
									'bg-orange-700'
								)}
							>
								<Pencil2
									className={classNames(
										'text-gray-700',
										'wh-32'
									)}
								/>
							</div>
							<span
								className={classNames(
									'mt-8',
									'text-12',
									'text-orange-700',
									'text-center'
								)}
							>
								Drafts
							</span>
						</a>
					</Link> */}
				</div>
			</div>
		</>
	);
};

When I have the google recaptcha provider around my app, and multiple uploads going my app completely crashes. If I comment out recaptcha provider, it works perfectly. Not sure if its doing something weird with upload inputs.

fred-boink avatar Sep 01 '21 18:09 fred-boink

Hi @t49tran

in my app.tsx

<GoogleReCaptchaProvider 
	scriptProps={{ async: true, defer: true, appendTo: 'body' }} 
	reCaptchaKey={process.env.NEXT_PUBLIC_GOOGLE_CAPTCHA_KEY} 
	useEnterprise 
	> 
	<ToastProvider
				placement="top-center"
				components={{ Toast: Toaster }}
				autoDismiss
			>
				<IntercomProvider
					appId={process.env.NEXT_PUBLIC_INTERCOM_APP_ID}
				>
					<LoginOverlayProvider>
						<UnsupportedBrowserGuard>
							<DesktopGuard>
							<AuthProvider>
								<Component {...pageProps} />
							</AuthProvider>
						</DesktopGuard>
						</UnsupportedBrowserGuard>
					</LoginOverlayProvider>
				</IntercomProvider>
			</ToastProvider>
		</GoogleReCaptchaProvider> 

Then we have inputs for uploading files in another component

const UploadPostButton = ({ postDraft, saveDraft }) => {
	const fileUpload = useRef<HTMLInputElement>(null);
	const { uploadFile } = useUploadStore();

	const onFileSelected = async (
		fileUploadEvent: ChangeEvent<HTMLInputElement>
	) => {
		const { files } = fileUploadEvent.target;

		for (let i = 0; i < files.length; i++) {
			const file: File = files[i];
			let postItem: DraftPostItemType;
			let fileToUpload;

			if (file.type.split('/')[0] === PostItemTypes.VIDEO) {
				const isMultiPart = IsMultiPart(file);
				const { vaultId, vaultVideoPresignedUrl, key, uploadId } =
					await getVideoSignedUrl(file.name, isMultiPart);
				fileToUpload = {
					file,
					presignedUrl: vaultVideoPresignedUrl,
					key,
					uploadId,
					vaultId
				};
				postItem = new VideoDraftPostItem({
					vaultId,
					status: PostItemStatuses.LOCAL,
					uploadedFileName: file.name
				});
				await postItem.setEnd(file);
				await postItem.setLocalThumbnail(file);
			} else {
				const { vaultId, vaultImagePresignedUrl } =
					await getImageSignedUrl(file.name);

				fileToUpload = {
					file,
					presignedUrl: vaultImagePresignedUrl,
					vaultId
				};
				postItem = new ImageDraftPostItem({
					vaultId,
					status: PostItemStatuses.LOCAL,
					uploadedFileName: file.name
				});
				await postItem.cropImageFromFile(file);
			}
			await postItem.setMediaUrlFromFile(file);

			// if no post cover set
			// first post item as post cover
			if (!postDraft.postCover || postDraft.postItems.length === 0) {
				postDraft.postCover = postItem;
			}

			postDraft.postItems = postDraft.postItems.concat(postItem);
			uploadFile(fileToUpload);
		}

		await saveDraft(postDraft);
	};

	return (
		<>
			<div
				className={classNames(
					'flex',
					'items-center',
					'justify-center',
					// 'py-15',
					// 'flex-1/4',
					'w-full',
					'bg-white',
					'bg-opacity-5'
				)}
			>
				<div
					className={classNames(
						'flex',
						'justify-center',
						'py-40',
						'pwa:pb-safe-15',
						'space-x-20'
					)}
				>
					<div>
						<input
							type="file"
							// heic must be explicitely added
							accept="image/*,video/*,image/heic"
							ref={fileUpload}
							hidden
							multiple
							onChange={(e) => onFileSelected(e)}
						/>
						<button
							type="button"
							onClick={() => fileUpload.current.click()}
							className={classNames(
								'flex',
								'items-center',
								'flex-col'
							)}
						>
							<div
								className={classNames(
									'flex',
									'items-center',
									'justify-center',
									'rounded-full',
									'wh-80',
									'bg-blue-700'
								)}
							>
								<Add
									className={classNames(
										'wh-32',
										'text-gray-700'
									)}
								/>
							</div>
							{/* <p
								className={classNames(
									'mt-8',
									'text-12',
									'text-blue-700',
									'text-center'
								)}
							>
								Add
							</p> */}
						</button>
					</div>
					{/* <Link href="/posts?status=draft">
						<a
							className={classNames(
								'flex',
								'items-center',
								'flex-col'
							)}
						>
							<div
								className={classNames(
									'flex',
									'items-center',
									'justify-center',
									'rounded-full',
									'wh-80',
									'bg-orange-700'
								)}
							>
								<Pencil2
									className={classNames(
										'text-gray-700',
										'wh-32'
									)}
								/>
							</div>
							<span
								className={classNames(
									'mt-8',
									'text-12',
									'text-orange-700',
									'text-center'
								)}
							>
								Drafts
							</span>
						</a>
					</Link> */}
				</div>
			</div>
		</>
	);
};

When I have the google recaptcha provider around my app, and multiple uploads going my app completely crashes. If I comment out recaptcha provider, it works perfectly. Not sure if its doing something weird with upload inputs.

you shouldn't use it in your app.tsx for Nextjs. You should create HOC and wrap each page in it or you will get overloads every time.

image

Moranilt avatar Oct 05 '21 09:10 Moranilt