so creating a project that updates me on my data in firebase however i have console logged the data and its constantly coming through. How do i stop the loop essentially so it doesn't crash.
import "./App.css";
import "semantic-ui-css/semantic.min.css";
import { Container, Divider, Card } from "semantic-ui-react";
import Header from "./components/Header";
import Cards from "./components/Cards/index";
import { useState, useEffect } from "react";
import { AppProvider } from "./Context/Context.js";
import "firebase/compat/auth";
import {} from "firebase/firestore";
import * as FirestoreService from "./components/service/firebase";
import firebase from "@firebase/app-compat";
function App() {
const [user, setUser] = useState(null);
const [cloudFucntions, setCloudFucntions] = useState();
const [error, setError] = useState();
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
setUser(user);
const unsubscribe = FirestoreService.getCloudFunctionItems(
(querySnapshot) => {
const updatedCloundFunctions = querySnapshot.docs.map((docSnapshot) =>
docSnapshot.data()
);
setCloudFucntions(updatedCloundFunctions);
console.log(updatedCloundFunctions);
},
(error) => setError("list-item-get-fail")
);
return unsubscribe;
});
}, [cloudFucntions]);
return (
<AppProvider>
<div>
<Container>
<Header />
<Divider horizontal section>
Cloud Function Monitor
</Divider>
{user ? (
<Card.Group itemsPerRow="4">
{cloudFucntions &&
cloudFucntions.map((cloudFunction) => {
return (
<Cards
key={cloudFunction.id}
cloudFunction={cloudFunction}
></Cards>
);
})}
</Card.Group>
) : (
<h2> Please sign in using the button in the top right. </h2>
)}
</Container>
</div>
</AppProvider>
);
}
export default App;
I have tried using an empty array and not passing anything through however i get the errors:
Warning: Each child in a list should have a unique "key" prop.
Warning: Failed prop type: Invalid prop children
supplied to CardContent
, expected a ReactNode.
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
CodePudding user response:
You can't generally use a dependency for an effect that the effect ultimately updates, this is what leads to render looping. For the useEffect
hook, cloudFucntions
isn't referenced at all in the callback, so it's not a dependnecy. With firebase connections it's more common to use a mounting useEffect
hook, i.e. an empty dependency array.
I see also that the component doesn't unsubscribe from the onAuthStateChanged
check. Since the the second cloud functions fetching doesn't appear to be dependent on the user
, you should split these into their own effects and subscription clean ups.
useEffect(() => {
const unsubscribe = firebase.auth()
.onAuthStateChanged((user) => setUser(user));
return unsubscribe;
}, []);
useEffect(() => {
const unsubscribe = FirestoreService.getCloudFunctionItems(
(querySnapshot) => {
const updatedCloundFunctions = querySnapshot
.docs
.map((docSnapshot) => docSnapshot.data());
setCloudFucntions(updatedCloundFunctions);
},
(error) => setError("list-item-get-fail")
);
return unsubscribe;
}, []);
Warning: Each child in a list should have a unique "key" prop.
This only means that key={cloudFunction.id}
isn't a unique enough id to be used as a React key. React keys must be unique among siblings. Don't use the array index as key unless you've no other choice. It's not recommended.
If your cloud function objects have duplicate ids, or there are duplicate cloud functions being returned you should fix this in the backend. If this isn't something you can resolve in the backend then you'll need to generate unique ids when you receive them in the frontend.
Example:
import { v4 as uuidV4 } from 'uuid';
...
const updatedCloundFunctions = querySnapshot
.docs
.map((docSnapshot) => ({
guid: uuidV4(), // <-- generate your own GUID
...docSnapshot.data(),
}));
setCloudFucntions(updatedCloundFunctions);
...
cloudFucntions.map((cloudFunction) => {
return (
<Cards
key={cloudFunction.guid} // <-- use your GUID here as key
cloudFunction={cloudFunction}
/>
);
})}
Warning: Failed prop type: Invalid prop children supplied to CardContent, expected a ReactNode.
Warning: Functions are not valid as a React child.
For each of these we'd need to see the Cards
component I suspect. The warnings are with regard to a CartContent
component it seems. You can add the Cards
component and other relevant code, along with any propTypes definitions and we can further refine answer, or you can post a new question on SO since these are rather extra-curricular to the issue of the render looping and React keys.
CodePudding user response:
You have an incorrect dependency on the useEffect. The call to setCloudFucntions
will retrigger useEffect (because it sets cloudFucntions
)
useEffect(() => {
...
setCloudFucntions(updatedCloundFunctions);
...
}, [cloudFucntions]);
You don't need it, your effect does not use cloudFucntions
.
The warning is concerning the key in the render, it seems that cloudFunction.id may not be unique.
You could do this
cloudFucntions.map((cloudFunction, idx) => {
return (
<Cards
key={idx}
cloudFunction={cloudFunction}
></Cards>
)