Uploading Images to Firebase through a React App - reactjs

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

Firebase image upload is not working i don't have any error

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.

currentUser uid undefined with getAuth hook

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?

How to store download urls and retrieve them from a collection

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]);

React Native: API Function making multiple calls to API server

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)}
/>
)
...

NextJS / next-translate : get lang outside of components

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();
}, []);

Resources