I have the following component that renders a clickable link for file upload
import { gql, useLazyQuery, useMutation } from '@apollo/client'; import { useEffect, useState } from 'react'; import type FileUpload from '@app/data/models/FileUpload'; import { ExternalLink } from '@app/mcui/components/atoms/business-process/shared/ExternalLink'; import { isNonEmptyString } from '@divvy-homes/utils'; export type Props = { uploadId: FileUpload['id']; }; const FILE_UPLOAD_QUERY = gql` query ($id: UUID!) { getFileUpload(id: $id) { id fileName url } } `; const SIGN_FILE_MUTATION = gql` mutation ($url: String!) { signAdminUploadUrl(url: $url) } `; export const FileUploadLink = ({ uploadId }: Props) => { const [fileUrl, setFileUrl] = useState<string>(); const [fileName, setFileName] = useState<string>(); const [getFileData] = useLazyQuery<{ getFileUpload: { url: string; fileName: string; }; }>(FILE_UPLOAD_QUERY, { onError: console.error, onCompleted: (data) => { setFileName(data.getFileUpload.fileName); setFileUrl(data.getFileUpload.url); }, variables: { id: uploadId, }, }); useEffect(() => { void getFileData({ variables: { uploadId } }); }, [getFileData, uploadId]); const [createSignedDocumentUrl] = useMutation<{ signAdminUploadUrl: string }>( SIGN_FILE_MUTATION, { onError: console.error, onCompleted: (urlData) => { const signedUrl = urlData.signAdminUploadUrl; window.open(signedUrl, '_blank', 'noreferrer'); }, }, ); return isNonEmptyString(fileUrl) ? ( <ExternalLink onClick={() => void createSignedDocumentUrl({ variables: { url: fileUrl } })} text={fileName ?? ''} /> ) : undefined; };
Every time I upload a file using this component I get a react error More hooks were rendered than during the last render.
. React indicates that the order of hooks has changed as follows
client.js:1 Warning: React has detected a change in the order of Hooks called by BusinessProcessDetails. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks Previous render Next render ------------------------------------------------------ 1. useContext useContext 2. useContext useContext 3. useContext useContext 4. useContext useContext 5. useRef useRef 6. undefined useState
Looking at my code, I can't understand why this error occurs. React says it's the first line of the component that calls useState that causes the error, but that makes no sense to me. Calling a reactive hook without a condition causes this, and the first line of the component that throws this error does not match the order in which the hook changed.
I'm using react 18.2.0, typescript 4.9.5
P粉1484347422023-09-10 20:47:31
It turns out that the problem lies in the way the component is called. The parent component rendering FileUploadLink
looks like this
export const MyComponent = ({ fileUpload }: MyProps) => { const values = [ { label: 'MyLabel', value: fileUpload ? FileUploadLink({ uploadId: fileUpload }) : undefined, }, ]; return ( <> {values.map((l, v) => { <div>{l}</div>; { v; } })} </> ); };
The fix is to wrap the component in a tag, i.e.
const values = [ { label: 'MyLabel', value: fileUpload ? <FileUploadLink uploadId={fileUpload} /> : undefined, }, ];