如何在React的useCallback钩子中正确使用useAuthenticatedFetch?解决调用错误
问题:如何在useCallback中正确使用Shopify的useAuthenticatedFetch钩子?
我持续收到如下错误,useAuthenticatedFetch是Shopify提供的钩子,返回原生JS的fetch方法,请问怎么在useCallback里正确调用它?
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> ); }
错误原因
React钩子规则明确要求:钩子只能在函数组件的顶层作用域调用,或在自定义钩子的顶层调用,不能嵌套在普通回调、条件判断、循环等逻辑内部。你在useCallback的回调函数里调用useAuthenticatedFetch,违反了这个规则,因此触发无效钩子调用错误。
正确实现方案
在组件顶层调用useAuthenticatedFetch拿到认证后的fetch方法,再在useCallback的回调中使用这个方法:
function DropZoneExample() { const [files, setFiles] = useState([]); // 1. 在组件顶层调用钩子,获取认证后的fetch实例 const authenticatedFetch = 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 }; // 2. 使用拿到的authenticatedFetch发起请求 await authenticatedFetch('/api/files', options) }, [authenticatedFetch], // 3. 将authenticatedFetch加入依赖数组,确保回调拿到最新实例 ); 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> ); }
关键改动说明
- 将
useAuthenticatedFetch()移到组件顶层,获取认证后的fetch实例authenticatedFetch - 在
handleDropZoneDrop中调用authenticatedFetch代替直接调用钩子 - 将
authenticatedFetch加入useCallback的依赖数组,确保回调能拿到最新的fetch实例
内容的提问来源于stack exchange,提问作者pyRabbit
相关产品推荐
相关产品推荐

