I'm trying to save fetched data into variable, but I always get "too many rerenders" or "undefined". What I'm doing wrong
import {
child,
get,
getDatabase,
ref,
} from "firebase/database";
const db = getDatabase();
function App() {
const [data, setData] = useState();
const getData = ref(db);
useEffect(() => {
const fetch = () => {
get(child(getData, "tokens/")).then((snapshot) => {
const fetched = snapshot.val();
setData(fetched);
});
setTimeout(() => {
console.log(data);
}, 500);
};
fetch();
}, []);
}
There's no need of setTimeout(). You can print the data when the promise is resolved as shown below:
function App() {
const [data, setData] = useState();
const getData = ref(db);
useEffect(() => {
const fetchData = () => {
get(child(getData, "tokens/")).then((snapshot) => {
const fetched = snapshot.val();
console.log(fetched)
setData(fetched);
});
};
fetchData();
}, []);
}
Also I've renamed the fetch function to avoid any confusion with Fetch API
Related
I am trying to execute a function to update a setState but it as well needs other state to load first.
const [chatsIds, setChatsIds] = useState([]);
const [chats, setChats] = useState([]);
useEffect(() => {
getChatsIds();
}, []);
useEffect(() => {
getChats();
}, [chats]);
the "getChats" needs the value from "chatsIds" but when the screen is loaded the value isn't , only when i reload the app again it gets the value.
Here are the functions :
const getChatsIds = async () => {
const ids = await getDoc(userRef, "chats");
setChatsIds(ids);
}
const getChats = async () => {
const chatsArr = [];
chatsIds.forEach(async (id) => {
const chat = await getDoc(doc(db, "Chats", id));
chatsArr.push(chat);
console.log(chatsArr);
});
setChats(chatsArr);
}
I've tried with the useEffect and useLayoutEffect hooks, with promises and async functions, but i haven't found what i'm doing wrong :(
The problem is in your useEffect hook dependency. It should depends on chatsIds not chats.
useEffect(() => {
getChats();
}, [chatsIds]);
Which mean fetching chatsIds should depend on first mount and fetching chats should depend on if chatsIds is chnaged.
You simply change the useEffect hook to like below.
useEffect(() => {
getChatsIds();
}, [chatsIds]);
I Think getChat() is depend on chatIds...
so you use useEffect with chatIds on dependency
const [chatsIds, setChatsIds] = useState([]);
const [chats, setChats] = useState([]);
useEffect(() => {
getChatsIds();
}, []);
useEffect(() => {
getChats(chatsIds);
}, [chatsIds]);
const getChatsIds = async () => {
const ids = await getDoc(userRef, "chats");
setChatsIds(ids);
}
const getChats = async (chatsIds) => {
const chatsArr = [];
chatsIds.forEach(async (id) => {
const chat = await getDoc(doc(db, "Chats", id));
chatsArr.push(chat);
console.log(chatsArr);
});
setChats(chatsArr);
}
The code below is a simplified example of my problem. There is a lot more going on in the actual codebase, so let's just assume that my useHook function must be asynchronous and we cannot just fetch the data inside the useEffect hook.
It currently renders {}, but I want it to render "Data to be displayed"
const fetch = async () => {
/* This code can't be changed */
return "Data to be displayed"
}
const useFetch = async () => { // This function must be asynchronous
let data = await fetch();
return data;
};
const App = () => {
const data = useFetch();
const [state, setState] = useState(data);
useEffect(() => {
setState(data);
}, [data]);
return <h1>{JSON.stringify(state)}</h1>
}
export default App;
change
const data = useFetch(); to const data = await useFetch();
Move called inside the useEffect like this:
const App = () => {
const [state, setState] = useState({});
useEffect(() => {
const fetchData = async () => {
const data = await useFetch();
setState(data);
}
fetchData()
}, []); // [] load first time
return <h1>{JSON.stringify(state)}</h1>
}
I want to put the data in Firebase between li tags. For example; Room Code: {roomCode} but I cannot access the data I received from Firebase as I want.
This is the only way I can see the data in the console:
import React, {useState, useEffect, Component} from "react";
import fire from './fire';
function DataConnection() {
const [rooms, setRooms] = useState();
useEffect(() => {
const db = fire.firestore();
return db.collection('rooms').onSnapshot((snapshot) => {
const postData = [];
snapshot.forEach((doc) => postData.push({...doc.data(), id:doc.id}));
setRooms(postData);
});
}, []);
console.log(rooms);
return(
<div>
</div>
);};
export default DataConnection;
Theres nowhere in the code you are mapping the results of rooms into you return statement
function DataConnection() {
const [rooms, setRooms] = useState();
useEffect(() => {
const db = fire.firestore();
return db.collection('rooms').onSnapshot((snapshot) => {
const postData = [];
snapshot.forEach((doc) => postData.push({...doc.data(), id:doc.id}));
setRooms(postData);
});
}, []);
console.log(rooms);
rooms.map((el) => {
return (<><li>Roomname: { el.roomName} </li><li>Roomcode: {el.roomCode}</li></>)
}
I want to use useReducer instead of useState for data that is updated using useEffect in the codes below because this causes too much rerenders when they are used in a condition to update itself
const [complete, setComplete] = useState("");
const [userProfile, setUserProfile] = useState("");
const [displayName, setDisplayName] = useState("");
const [displayPicture, setDisplayPicture] = useState("");
useEffect(() => {
if (user.uid) {
const onChildAdd = database()
.ref("/User/" + user.uid)
.on("value", (snapshot) => {
setComplete(snapshot.val().Complete);
setUserProfile(snapshot.val().User);
setDisplayName(snapshot.val().displayName);
setDisplayPicture(snapshot.val().photoURL);
// ...
});
return () =>
database()
.ref("/User/" + user.uid)
.off("value", onChildAdd);
}
}, []);
below is a condition I am trying to use
function CheckInfo() {
if (!complete) {
setComplete("complete");
} else if (!displayName) {
setDisplayName("myName");
}
}
I tried to create a function for fetching data from the server, and it works.
But I am not sure if that the right way?
I created a function component to fetching data, using useState, useEffect and Async/Await :
import React, { useState, useEffect } from "react";
const Fetch = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
let res = await fetch(
"https://api.coindesk.com/v1/bpi/currentprice.json" //example and simple data
);
let response = await res.json();
setData(response.disclaimer); // parse json
console.log(response);
};
fetchData();
}, []);
return <div>{data}</div>;
};
export default Fetch; // don't run code snippet, not working, this component must be imported in main
Where I am not sure is a place where to call the fetchData function. I do that inside useEffect? Right place? And, this call will happen only one? Because i use []?
Generally, how would you do something like this?
Overall, you are heading in the right direction. For fetching data, you'd wanna use useEffect and pass [] as a second argument to make sure it fires only on initial mount.
I believe you could benefit from decoupling fetchJson function and making it more generic, as such:
const fetchJson = async (url) => {
const response = await fetch(url);
return response.json();
};
const Fetch = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetchJson("https://api.coindesk.com/v1/bpi/currentprice.json")
.then(({ disclaimer }) => setData(disclaimer));
}, []);
return <div>{data}</div>;
};
Another option is to use a self invoking function:
const Fetch = () => {
const [data, setData] = useState(null);
useEffect(() => {
(async () => {
let res = await fetch(
"https://api.coindesk.com/v1/bpi/currentprice.json" //example and simple data
);
let response = await res.json();
setData(response);
})();
}, []);
return <div>{data}</div>;
};
The suggestion to separate out the fetch logic to a separate function is a good idea and can be done as follows:
const Fetch = () => {
const [data, setData] = useState(null);
useEffect(() => {
(async () => {
let response= await fetchData("https://api.coindesk.com/v1/bpi/currentprice.json");
setData(response);
})();
}, []);
return <div>{data}</div>;
};
const fetchData = async (url) => {
const response = await fetch(url);
const json = await response.json();
return json;
};
And yet another option is to create a wrapper function around useEffect that triggers the async function for you similar to this:
export function useAsyncEffect(effect: () => Promise<any>) {
useEffect(() => {
effect().catch(e => console.warn("useAsyncEffect error", e));
});
}