React+Redux应用:Firebase图片URL写入数据库丢失问题求助
Hey there! Let's break down what's going wrong with your React+Redux recipe app and fix it step by step. The core problem here is that even though you're using await for your image uploads, the timing or promise handling is off—so your Firebase database and Redux store aren't getting the updated imageUrls even though the logger shows them correctly.
Common Culprits & Fixes
1. Your uploadImages Function Isn't Properly Returning Resolved URLs
First, let's make sure your image upload function is actually waiting for all uploads to finish and returning the full array of URLs. A common mistake is using forEach (which doesn't play nice with async/await) instead of map + Promise.all.
Here's a corrected version of uploadImages:
const uploadImages = async (imagesToAdd, recipeKey) => { if (!imagesToAdd || imagesToAdd.length === 0) return []; // Map each image to an upload promise, then wait for all to complete const uploadPromises = imagesToAdd.map(async (image) => { const storageRef = firebase.storage().ref(`recipes/${recipeKey}/${image.name}`); await storageRef.put(image); // Wait for upload to finish return storageRef.getDownloadURL(); // Return the URL promise }); // Wait for ALL uploads to resolve before returning the URL array return Promise.all(uploadPromises); };
2. Your startAddRecipe Action Isn't Waiting for Updates to Complete
Next, let's fix the action to ensure it waits for the image uploads, updates the Firebase database, and then dispatches the correct data to Redux. You need to await the database update and make sure you're passing the full imageUrls array when dispatching.
Here's the revised thunk action:
export const startAddRecipe = (recipeData) => { return async (dispatch) => { const { imagesToAdd, ...restRecipeData } = recipeData; // Step 1: Create initial recipe in Firebase and get the key const recipesRef = firebase.database().ref('recipes'); const newRecipeRef = recipesRef.push(restRecipeData); const recipeKey = newRecipeRef.key; try { let imageUrls = []; // Step 2: Upload images (wait for completion!) if (imagesToAdd?.length > 0) { imageUrls = await uploadImages(imagesToAdd, recipeKey); console.log('Fetched Image URLs:', imageUrls); // This should now be a full array } // Step 3: Update the Firebase recipe with imageUrls (await this!) await newRecipeRef.update({ imageUrls }); // Step 4: Dispatch the FULL recipe data to Redux dispatch(addRecipe({ id: recipeKey, ...restRecipeData, imageUrls })); } catch (err) { console.error('Failed to add recipe:', err); // Optional: Clean up partial data (e.g., delete uploaded images/recipe entry) } }; };
3. Double-Check Your Redux Reducer
Make sure your reducer is correctly handling the ADD_RECIPE action and returning a new state array with the updated recipe. It's easy to accidentally mutate state or forget to include imageUrls:
const recipesReducer = (state = [], action) => { switch (action.type) { case 'ADD_RECIPE': // Return a NEW array with the new recipe (never mutate state!) return [...state, { ...action.recipe }]; // Handle other actions here... default: return state; } };
4. Verify Firebase Database Rules
Don't forget to check your Firebase Realtime Database rules—if you don't have write permissions for updating existing entries, the update will fail silently. A temporary test rule (not for production!) would look like:
{ "rules": { ".read": true, ".write": true } }
Why This Works
The key fixes here are:
- Using
Promise.allto wait for all image uploads to finish before proceeding await-ing the Firebase database update to ensure it completes before dispatching to Redux- Making sure your reducer returns a new state object with the full recipe data (including
imageUrls)
内容的提问来源于stack exchange,提问作者Voytek

