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));
});
}
Related
I have an api (an arr of objects) which I need to pass into a state, so that I can then pass that data inside a component to show it on the website.
1st approach:
// pulls the api data
const newData = axios.get(url).then((resp) => {
const apiData = resp.data;
apiData.map((video) => {
return video;
});
});
// sets the state for the video
const [selectedVideo, setSelectedVideo] = useState(newData[0]);
const [videos] = useState(videoDetailsData);
...
return (
<>
<FeaturedVideoDescription selectedVideo={selectedVideo} />
</>
)
2nd approach:
const useAxiosUrl = () => {
const [selectedVideo, setSelectedVideo] = useState(null);
useEffect(() => {
axios
.get(url)
.then((resp) => setSelectedVideo(resp.data))
});
return selectedVideo;
}
...
return (
<>
<FeaturedVideoDescription selectedVideo={selectedVideo} />
</>
)
both of these approaches don't seem to work. What am I missing here?
The correct way is to call your axios method inside the useEffect function.
const fetchData = axios.get(url).then((resp) => setSelectedVideo(resp.data)));
useEffect(() => {
fetchData();
}, [])
or if you need async/await
useEffect(() => {
const fetchData = async () => {
const response = await axios.get(url);
setSelectedVideo(resp.data);
}
fetchData();
}, [])
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
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 can console.log value of data in getCurrency funuction.but when i try to put data into setCurrency and get it from currency it doesn't work.its just empty object then.
import React ,{useState, useEffect} from 'react';
import TranslateForm from './translateComponent';
const Main = () => {
const API_KEY = 'b8533a4d2a2297728b70';
const [rcurrency, setRcurrency] = useState('USD');
const [ccurrency, setCcurrency] = useState('LKR');
const [query, setQuery] = useState();
const [currency, setCurrency] = useState({});
useEffect(() => {
setCurrency(getCurrency());
console.log(currency);
},[]);
useEffect(
() => {
getQuery();
},[query]
);
const getCurrency = async() => {
const responce = await fetch(`https://free.currconv.com/api/v7/currencies?apiKey=${API_KEY}`);
const data = await responce.json();
console.log(data);
setCurrency(data);
}
const getQuery = async() => {
const responce = await fetch(`https://free.currconv.com/api/v7/convert?q=${rcurrency}_${ccurrency},${ccurrency}_${rcurrency}&compact=ultra&apiKey=${API_KEY}`);
const qdata = await responce.json();
setQuery(qdata[Object.keys(qdata)[0]]);
console.log(query);
}
return(
<div>
<TranslateForm/>
</div>
);
}
export default Main;
Actually the issue is your function is not returning any data in order to set that in the setCurrency method.
Either set data in your getCurrency() method or return the data from getCurrency and set in useEffect().
useEffect(() => {
const respCurrency = getCurrency();
respCurrency.then((data)=>{
setCurrency(respCurrency);
})
console.log(currency); // this will always give you default value
},[]);
const getCurrency = async() => {
const responce = await fetch(`https://free.currconv.com/api/v7/currencies?apiKey=${API_KEY}`);
const data = await responce.json();
console.log(data);
setCurrency(data);
return data;
}
return(
<div>
Data : JSON.stringify(currency) // Your currency logging
<TranslateForm/>
</div>
);
Also since getCurrency is an async funtion it will most probably be returning you a promise object.So use .then() method to assign to the setCurrency method.
Try consoling currency now in your render method you will get the data.
I hope this works.
Thanks