ReactJS Uppy技术问题:状态变更后Uppy重置状态导致图片不显示,如何预填充/预加载图片
Hey there! Let's work through your Uppy issues step by step. The core problem you're hitting is that your Uppy instance gets reinitialized every time your component state changes, which wipes out any uploaded files. Let's fix that first, then cover how to properly add/display images and implement preloading.
1. Stop Uppy from resetting on component state changes
The key here is to persist your Uppy instance across component re-renders using React's useRef—this keeps the instance alive instead of creating a new one every time your component updates. Here's how to adjust your code:
import { useRef, useEffect } from 'react'; import Uppy from '@uppy/core'; import XHRUpload from '@uppy/xhr-upload'; import Webcam from '@uppy/webcam'; import Dashboard from '@uppy/react'; function YourComponent() { // Use useRef to keep the Uppy instance stable across re-renders const equipmentUppyRef = useRef(null); const data = /* Your existing data state here */; useEffect(() => { // Initialize Uppy ONLY once when the component mounts if (!equipmentUppyRef.current) { const uppy = new Uppy({ id: 'equipment_uppy', restrictions: { allowedFileTypes: ['image/*', 'video/*'] }, autoProceed: false }); uppy.use(XHRUpload, { endpoint: `${process.env.REACT_APP_SITE_URL}/api/auth/uploadavintakeimages`, formData: true, fieldName: 'files[]', headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }); uppy.use(Webcam); uppy.on('file-added', (file) => { uppy.setFileMeta(file.id, { type: file.source, avintakeid: data.id }); }); equipmentUppyRef.current = uppy; } // Update dynamic values (like auth token or avintakeid) without resetting Uppy const uppy = equipmentUppyRef.current; if (uppy) { // Refresh auth header if access token changes const uploadPlugin = uppy.getPlugin('XHRUpload'); if (uploadPlugin) { uploadPlugin.setOptions({ headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` } }); } // Update avintakeid meta for all files if data.id changes uppy.getFiles().forEach(file => { uppy.setFileMeta(file.id, { ...file.meta, avintakeid: data.id }); }); } // Clean up Uppy instance when component unmounts to avoid memory leaks return () => { if (equipmentUppyRef.current) { equipmentUppyRef.current.close(); equipmentUppyRef.current = null; } }; }, [data.id]); // Re-run effect only when data.id changes return ( <Dashboard uppy={equipmentUppyRef.current} id='equipment_location_image' width='100%' height='300px' plugins={['Webcam']} /> ); }
2. Adding and displaying uploaded images correctly
The Uppy Dashboard will automatically display previews for added/uploaded images, but if you want to show them outside the Dashboard (like in a gallery), you can fetch the file list from Uppy and render them directly:
// Inside your component const uploadedImages = equipmentUppyRef.current?.getFiles() .filter(file => file.status === 'upload-success'); // In your JSX return ( <> <Dashboard {/* ... your existing Dashboard props ... */} /> <div className="uploaded-image-gallery"> {uploadedImages?.map(file => ( <img key={file.id} src={file.preview} alt={file.name} style={{ width: '120px', height: '120px', objectFit: 'cover', margin: '8px' }} /> ))} </div> </> );
3. Pre-filling and preloading existing images
If you need to load images that were already uploaded (e.g., on an edit page), use Uppy's addFile method to inject these files into the instance. Mark them as "already uploaded" so Uppy doesn't try to re-send them:
useEffect(() => { // ... existing Uppy initialization code ... // Fetch and preload existing images from your API const preloadExistingImages = async () => { if (!data.id) return; // Replace with your API call to get existing images for the current data.id const existingImages = await fetch(`/api/auth/getavintakeimages/${data.id}`) .then(res => res.json()); existingImages.forEach(img => { equipmentUppyRef.current.addFile({ id: img.id, // Unique ID for the existing file name: img.filename, type: img.mimeType, preview: img.url, // URL to the hosted image for preview meta: { avintakeid: data.id, url: img.url, // Store the existing URL for reference type: 'existing' // Optional: mark to handle differently (e.g., delete logic) }, status: 'upload-success' // Tell Uppy this file is already uploaded }); }); }; // Run preload after Uppy is initialized and data.id is available if (equipmentUppyRef.current && data.id) { preloadExistingImages(); } // ... cleanup code ... }, [data.id]);
If you want users to be able to delete preloaded images, add a listener for the file-removed event to trigger your API's delete endpoint:
uppy.on('file-removed', async (file) => { if (file.meta.type === 'existing') { // Call your API to delete the existing image await fetch(`/api/auth/deleteavintakeimage/${file.meta.url}`, { method: 'DELETE' }); } });
内容的提问来源于stack exchange,提问作者Chirag Kasera

