reactjspolaris

How do you use fetch insde of React's useCallback hook?


I am constantly receiving the following error -- how do I properly use the useAuthenticatedFetch method within the useCallback hook? For those not familiar with Shopify, the useAuthenticatedFetch method returns the native JS fetch method.

 Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
function DropZoneExample() {
    const [files, setFiles] = useState([]);
    
    const handleDropZoneDrop = useCallback( async (_dropFiles, acceptedFiles, _rejectedFiles) => {
            setFiles((files) => [...files, ...acceptedFiles]);
            let formData = new FormData();
            formData.append('files', acceptedFiles)
            let options = {
                method: 'POST',
                body: formData
            };
            await useAuthenticatedFetch('/api/files', options)
        },
        [],
    );

    const validImageTypes = ['image/gif', 'image/jpeg', 'image/png'];

    const fileUpload = !files.length && <DropZone.FileUpload />;
    const uploadedFiles = files.length > 0 && (
        <div style={{padding: '0'}}>
            <Stack vertical>
                {files.map((file, index) => (
                    <Stack alignment="center" key={index}>
                        <Thumbnail
                            size="small"
                            alt={file.name}
                            source={
                                validImageTypes.includes(file.type)
                                    ? window.URL.createObjectURL(file)
                                    : NoteMinor
                            }
                        />
                        <div>
                            {file.name}{' '}
                            {file.size} bytes
                        </div>
                    </Stack>
                ))}
            </Stack>
        </div>
    );

    return (
        <DropZone onDrop={handleDropZoneDrop} dropOnPage={true}>
            {uploadedFiles}
            {fileUpload}
        </DropZone>
    );
}

Solution

  • What you want is to access the fetch method from the hook instead. You assigned the returned function from the hook outside of your useCallback as such:

    const fetch = useAuthenticatedFetch();
    
    const handleDropZoneDrop = useCallback( async (_dropFiles, acceptedFiles, _rejectedFiles) => {
            setFiles((files) => [...files, ...acceptedFiles]);
            let formData = new FormData();
            formData.append('files', acceptedFiles)
            let options = {
                method: 'POST',
                body: formData
            };
            await fetch('/api/files', options);
        },
        [],
    );