I created a photo gallery app using React. When I upload an Image in the App, it is storing two documents of the image in the firestore, containing exactly the same 'created At' and 'url', but under two difference IDs. I want it to store only 1 document. I am not able to understand why my code is running twice and uploading the image twice?
Uploading is done using 'useStorage' custom hook:
import { useState, useEffect } from 'react'
import { projectStorage, projectFirestore, timestamp } from '../firebase/config'
const useStorage = (file) => {
const [progress, setProgress] = useState(0)
const [error, setError] = useState(null)
const [url, setUrl] = useState(null)
useEffect(() => {
// references
const storageRef = projectStorage.ref(file.name)
const collectionRef = projectFirestore.collection('images')
// uploading the file to the reference
storageRef.put(file).on(
'state_changed',
(snap) => {
let percentage = (snap.bytesTransferred / snap.totalBytes) * 100
setProgress(percentage)
},
(err) => {
setError(err)
},
async () => {
const url = await storageRef.getDownloadURL()
const createdAt = timestamp()
await collectionRef.add({ url, createdAt })
setUrl(url)
}
)
}, [file])
return { progress, url, error }
}
export default useStorage
'useFirestore' custom hook:
import { useEffect, useState } from 'react'
import { projectFirestore } from '../firebase/config'
const useFirestore = (collection) => {
const [docs, setDocs] = useState([])
useEffect(() => {
// return a function to un-subscribe from the collection
const unsub = projectFirestore
.collection(collection)
.orderBy('createdAt', 'desc')
.onSnapshot((snap) => {
let documents = []
snap.forEach((doc) => {
documents.push({ ...doc.data(), id: doc.id })
})
setDocs(documents)
})
// clean-up function
return () => unsub()
}, [collection])
return { docs }
}
export default useFirestore
Remove Strict Mode in index.js file, that is causing upload of file 2 times.
Related
I am trying to create app with react and I want to upload multiple image in firebase storage everything is going fine but my image are not uploading i can't figure out why below is my code for upload form
import React, { useEffect, useState } from "react";
import { doc, setDoc } from "firebase/firestore";
import { getFirestore } from "firebase/firestore";
import {
getStorage,
ref,
uploadBytesResumable,
getDownloadURL,
} from "firebase/storage";
import { firebaseApp } from "../../../firebase.config";
export default function UploadForm() {
const [imageAsset, setImageAsset] = useState([]);
const [urls, setUrls] = useState([]);
const storage = getStorage(firebaseApp); // Firebase Storage
const firebaseDb = getFirestore(firebaseApp); // Firebase FireStore
function UploadImage(e) {
e.preventDefault();
setLoading(true);
for (let i = 0; i < e.target.files.length; i++) {
const Images = e.target.files[i];
Images["id"] = Math.random();
setImageAsset((prevState) => [...prevState, Images]);
}
const promises = [];
imageAsset.map((image) => {
const storageRef = ref(storage, `Images/${Date.now()}-${image.name}}`);
const uploadTask = uploadBytesResumable(storageRef, image);
promises.push(uploadTask);
uploadTask.on(
"state_changed",
(snapShot) => {
const uploadProgress =
(snapShot.bytesTransferred / snapShot.totalBytes) * 100;
setProgress(uploadProgress);
},
(error) => {
console.log(error);
},
async () => {
await storage
.ref("Images")
.child(image.name)
.getDownloadURL()
.then((urls) => {
setUrls((prevState) => [...prevState, urls]);
setLoading(false);
});
}
);
Promise.all(promises)
.then(() => alert("done"))
.catch((err) => console.log(err));
});
}
useEffect(() => {}, [title, description, imageAsset]);
return (
<input type={"file"} multiple onChange={UploadImage} />
);
}
In console it shows 1 as a progress even i didn't select any image i don't understand why? it was perfectly working before when i was uploading single image.
I'm a freshman in college and currently beginning with react and firebase in my free time. There is one thing I don't know why it doesn't works in my project.
const currentUser = useAuth()
const { documents: books } = useCollection("books", ["uid", "==", currentUser.uid])
the problem is that when i console i get ["uid", "==", undefined]
This is my useAuth hook
import { useState, useEffect } from 'react'
import { onAuthStateChanged } from "firebase/auth";
import { auth } from '../firebase/config'; //this is getAuth()
export function useAuth() {
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => setCurrentUser(user));
return unsub;
}, [])
return currentUser;
}
and this is my hook to collect data from firestore
import { useState, useEffect, useRef } from "react"
import { db } from "../firebase/config" //this is getFirestore()
//firebase imports
import { collection, onSnapshot, query, where} from "firebase/firestore"
export const useCollection = (col, _q) => {
const [error, setError] = useState(null)
const [documents, setDocuments] = useState(null)
//set up query
const q = useRef(_q).current
useEffect(() => {
setError(null)
let ref = collection(db, col)
if (q) {
ref = query(ref, where(...q))
}
const unsub = onSnapshot(ref, (snapshot) => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ id: doc.id, ...doc.data() })
})
setDocuments(results)
}, (err) => {
console.log(err.message)
setError(err.message)
})
return () => unsub()
}, [col, q])
return { documents, error }
}
I thought about something with sync or async, but could not find it.
Would someone have a solution and explain it to me?
I have a form that sends data and images to firebase (firestore). I created a collection that only stores the urls. What I need is a way to query the different images urls based on a document reference ID because in my hierarchy, the last collection creates documents with unique ID and I'm unable to query them in order to get the image url.
Form.js
import { useSelector } from "react-redux";
import { db, storage } from "../../firebase";
import {
addDoc,
collection,
doc,
updateDoc,
} from "#firebase/firestore";
import { getDownloadURL, ref, uploadBytes } from "#firebase/storage";
import { useSession } from "next-auth/react";
function Form() {
const { data: session } = useSession();
const Images = useSelector((state) => state.draggedImages.images);
const imageTarget = Images.length - 1;
const SendPost = async () => {
const docRef = await addDoc(collection(db, "posts"), {
id: session.user.uid,
AdDescription: description,
});
Images[imageTarget].map((Img) => {
const imageRef = ref(storage, `posts/${docRef.id}/${Img.name}`);
uploadBytes(imageRef, Img, "data_url").then(async () => {
const downloadURL = await getDownloadURL(imageRef);
await updateDoc(doc(db, "posts", docRef.id), {
image: downloadURL,
});
// ---------------HERE IS THE PROBLEM--------------
await addDoc(collection(db, "ImageUrl", docRef.id, "Urls"), {
image: downloadURL,
});
// --------------------------------------------------
});
});
};
}
export default Form;
upon uploading the images, I have to fetch them into a carousel.
Carousel.js
import {
collection,
doc,
onSnapshot,
orderBy,
query,
getDocs,
} from "#firebase/firestore";
import { useRouter } from "next/router";
import React, { useEffect, useRef, useState } from "react";
import { db } from "../../firebase";
function Carousel() {
const [FetchedImages, setFetchedImages] = useState([]);
const router = useRouter();
const { id } = router.query;
useEffect(
() =>
onSnapshot(doc(db, `ImageUrl/${id}`), (snapshot) => {
setFetchedImages(snapshot.data());
}),
[db]
);
console.log("fetched : ", FetchedImages); // returns undefined
}
export default Carousel;
The defined hierarchy in the Form.js is pretty fine. The problem was actually the way to retrieve the data from Carousel.js using useEffect.
Following this resource , Here's the updated and working solution I used.
Carousel.js
useEffect(() => {
const FetchedImagesFromFirestore = async () => {
const querySnapshot = await getDocs(
collection(db, `ImageUrl/${id}/Urls`)
);
querySnapshot.forEach((doc) => {
setFetchedImages((prevState) => [...prevState, doc.data()]);
});
};
FetchedImagesFromFirestore();
}, [db]);
I'm using the Yelp API and Expo Location in React Native. My function for calling the API and getting the location is making 3 calls on each load. I'm guessing it's because my state is changing and causing the function to run again, but I can't seem to get it to stop.
Any thoughts on the hook below?
import React, { useEffect, useState } from 'react';
import yelp from '../api/yelp';
import * as Location from 'expo-location';
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const [location, setLocation] = useState({});
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
})();
}, []);
const searchAPI = async (defaultTerm) => {
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term: defaultTerm,
latitude: location.coords.latitude,
longitude: location.coords.longitude,
radius: 4000,
},
});
setResults(response.data.businesses);
} catch (error) {
setErrorMessage('Something went wrong 😢');
}
};
console.log(`latitude: ${location.coords.latitude}`);
console.log(`longitude: ${location.coords.longitude}`);
useEffect(() => {
searchAPI('');
}, []);
return [searchAPI, results, errorMessage];
};
The hook is being used here:
const [searchTerm, setSearchTerm] = useState('');
const [searchAPI, results, errorMessage] = useResults();
const filterResultsByPrice = (price) => {
return results.filter(result => {
return result.price === price;
});
};
return (
<View style={styles.resultsContainerStyle}>
<SearchBar
searchTerm={searchTerm}
onSearchTermChange={setSearchTerm}
onSearchTermSubmit={() => searchAPI(searchTerm)}
/>
)
...
We have a short question for our application (NextJS 11.0.0 + next-translate 1.0.7)
The library contains a function to make an API call (/lib/mylib.js) :
export const getDataExample = async (lang) => {
return fetch(_apiurl_/example/{lang});
};
And my component in react (/components/myComponent.js) call this function with a useEffect:
import { useEffect, useState } from 'react';
import useTranslation from 'next-translate/useTranslation';
import { getDataExample } from '/lib/mylib';
export default function MyComponent() {
const [data, setData] = useState(false);
const { lang } = useTranslation();
useEffect(() => {
const fetchData = async () => {
const response = await getDataExample(lang);
setData(response);
};
fetchData();
}, []);
[...]
}
I don't want to call getDataExample() directly with the lang parameter.
Is it possible to get the current language in the function (/lib/mylib.js) ?
Thank you for your reply !
But now imagine that my library (/lib/mylib.js) is also used to fetch data into a getServerSideProps :
export async function getServerSideProps({ locale }) {
const response = await getDataExample(locale);
[...]
}
React Hooks are not available here, so what do you do ?
You can create your custom hook. This is an example:
const useFetchWithLang = (func) => {
const { lang } = useTranslation()
return useCallback((args) => func({ ...args, lang }), [lang])
}
const fetchDataExample = ({ otherParam, lang }) => {
return { test: 'test1' }
}
const fetchDataExampleWithLang = useFetchWithLang(fetchDataExample)
After for example, you could use it in a useEffect.
useEffect(() => {
const fetchData = async () => {
const response = await fetchDataExampleWithLang({ otherParam: 'test' });
setData(response);
};
fetchData();
}, []);