i am trying to get data after each 10 seconds, i did it perfectly but the problem is my DOM renders first time i get data but as 10 seconds passed it did'nt update data and shows an error related to data...
here's the DOM error
CODE
App.js
import { Appbar } from './components/pageOne/Appbar'
import { Cards } from './components/pageOne/Cards'
import { fetchData } from './components/FetchDataFromApi'
function App() {
const [data, setData] = useState({})
const a = useEffect(() => {
let interval = setInterval(() => setData(), 10000)
return () => clearInterval(interval)
})
useEffect(() => {
(async () => {
const fetchedData = await fetchData();
setData(fetchedData)
})()
// let interval = setInterval(() => setData(), 10000)
// return () => clearInterval(interval)
}, [a])
return (
<>
<Appbar />
<Cards data = {data} />
</>
)
}
export default App;
index.js
const url = 'https://covid19.mathdro.id/api'
export const fetchData = async (country) => {
let changeableUrl = url
if (country) {
changeableUrl = `${url}/countries/${country}`
}
try {
const {data: {confirmed, recovered, deaths}} = await axios.get(changeableUrl)
return { confirmed,
recovered,
deaths
}
}
catch
(error) {
return error
}
}```
You're setting the data to undefined after 10 seconds -
let interval = setInterval(() => setData(), 10000)
You need to go and fetch the data again -
let interval = setInterval(async () => {
const fetchedData = await fetchData();
setData(fetchedData)
}, 10000);
destructing in fetchData function is not correct.
export const fetchData = async (country) => {
let changeableUrl = url
if (country) {
changeableUrl = `${url}/countries/${country}`
}
try {
const resp = await axios.get(changeableUrl);
const { confirmed, recovered, deaths } = resp.data;
return { confirmed,
recovered,
deaths
}
}
catch
(error) {
return error
}
}
useEffect is not used correctly. Here is the correction.
import { Appbar } from "./components/pageOne/Appbar";
import { Cards } from "./components/pageOne/Cards";
import { fetchData } from "./components/FetchDataFromApi";
function App() {
const [data, setData] = useState({});
useEffect(() => {
let interval = setInterval(async () => {
const newData = await fetchData();
setData(newData);
}, 10000);
return () => clearInterval(interval);
}, []);
return (
<>
<Appbar />
<Cards data={data} />
</>
);
}
export default App;
You can also checkout this sample code.
https://codesandbox.io/s/festive-lewin-vl9g0?file=/src/App.js
Related
Here is what i tried
episode.js
import Parser from "rss-parser";
import React from "react";
export default function Episode() {
const parser = new Parser();
const url1 = "https://anchor.fm/s/75abc654/podcast/rss";
const [data, setData] = React.useState({});
(async () => {
let data = await parser.parseURL(url1);
setData(data);
// console.log(data.title)
// data.items.forEach((item) => {
// console.log(item.title)
//console.log(item.pubDate.slice(5, 17))
//console.log(item.enclosure.url)
// console.log(item.itunes.image)
});
})();
return(
<h1>{item.title}</h1>
{data.items.map((item, index)=>{
return(
<h1>{item.title}</h1>
)})}
)
}
And the output is blank screen.. No error in console.. Help me to get the data from the rss feed without blank screen
You're calling async method unlimited time! You need to call it once via useEffect just when component rendered for first time:
import Parser from "rss-parser";
import React, { useEffect } from "react";
function Episode() {
const parser = new Parser();
const url1 = "https://anchor.fm/s/75abc654/podcast/rss";
const [data, setData] = React.useState({});
useEffect(() => {
(async () => {
let data = await parser.parseURL(url1);
console.log(data);
setData(data);
})();
}, []);
return (
<>
{data.items?.map((item, index) => (
<h1>{item.title}</h1>
))}
</>
);
}
export default function App() {
return <Episode />;
}
Just replace your async function with a useEffect hook like this
useEffect(() => {
async function fetchMyAPI() {
let data = await parser.parseURL(url1);
setData(data);
// console.log(data.title)
// data.items.forEach((item) => {
// console.log(item.title)
//console.log(item.pubDate.slice(5, 17))
//console.log(item.enclosure.url)
// console.log(item.itunes.image)
}
fetchMyAPI();
}, []);
This will be executed once every time when your component is loaded on screen
Also change the data.item.map to
{data.items?.map((item, index) => {
return <h1>{item.title}</h1>;
})}
Else it will throw error on first render
I am trying to filter my results via button onClicks in React, but for some reason its not working?
https://stackblitz.com/edit/react-x7tlxr?file=src/App.js
import React, {useEffect, useState} from "react";
import axios from 'axios';
import "./style.css";
export default function App() {
const [fetchData, setFetchData] = useState([]);
const [loading, setLoading] = useState(true);
const [isError, setIsError] = useState(false);
const url = 'https://jsonplaceholder.typicode.com/todos';
useEffect(() => {
let mounted = true;
const loadData = async () => {
try {
const response = await axios(url);
if (mounted) {
setFetchData(response.data);
setLoading(false);
setIsError(false);
console.log('data mounted')
}
} catch (err) {
setIsError(true)
setLoading(false);
setFetchData([]);
console.log(err);
}
};
loadData();
return () => {
mounted = false;
console.log('cleaned');
};
},
[url]
);
const renderdata = () => {
if (fetchData) {
return (
<div>{fetchData.map(inner => {
return (
<React.Fragment key={inner.id}>
<p>{inner.title}</p>
</React.Fragment>
)
})}</div>
)
} else {
<p>No data to display!</p>
}
}
const handle1 = () => {
const result1 = fetchData.filter((inner) => inner.id === '1');
setFetchData(result1);
}
const handle2 = () => {
const result2 = fetchData.filter((inner) => inner.id === '2');
setFetchData(result2);
}
const handleAll = () => {
setFetchData(fetchData);
}
return (
<div>
<button onClick={handleAll}>Show all</button>
<button onClick={handle1}>Filter by id 1</button>
<button onClick={handle2}>Filter by id 2</button>
{renderdata()}
</div>
);
}
The id is a number, not a string, so you filter(s) need to be changed like that:
const result1 = fetchData.filter((inner) => inner.id === 1);
You have another problem, is that you change the whole data set when you filter, so once you've filtered, you can't "unfilter" or filter again on another id.
You need to maintain the original fetchedData unchanged.
This example shows how it can be fixed.
I found 2 issues in your code
Id in the API result is numeric not string
inner.id === '2'. so this will return false inner.id === 2 you need to use like this
when you assign the filtered value to the original array it will of course change the original array so when you try to filter it second time you don't have the original API result in the fetch data array because it is already filtered
So i have created one more array filteredData
This will work.
import React, {useEffect, useState} from "react";
import axios from 'axios';
import "./style.css";
export default function App() {
const [fetchData, setFetchData] = useState([]);
const [filteredData, setFileredData] = useState([]);
const [loading, setLoading] = useState(true);
const [isError, setIsError] = useState(false);
const url = 'https://jsonplaceholder.typicode.com/todos';
useEffect(() => {
let mounted = true;
const loadData = async () => {
try {
const response = await axios(url);
if (mounted) {
setFetchData(response.data);
setFileredData(response.data)
setLoading(false);
setIsError(false);
console.log('data mounted')
}
} catch (err) {
setIsError(true)
setLoading(false);
setFetchData([]);
console.log(err);
}
};
loadData();
return () => {
mounted = false;
console.log('cleaned');
};
},
[url]
);
const renderdata = () => {
if (filteredData) {
return (
<div>{filteredData.map(inner => {
return (
<React.Fragment key={inner.id}>
<p>{inner.title}</p>
</React.Fragment>
)
})}</div>
)
} else {
<p>No data to display!</p>
}
}
const handle1 = () => {
const result1 = fetchData.filter((inner) => inner.id === 1);
setFileredData(result1);
}
const handle2 = () => {
const result2 = fetchData.filter((inner) => inner.id === 2);
setFileredData(result2);
}
const handleAll = () => {
setFileredData(fetchData);
}
return (
<div>
<button onClick={handleAll}>Show all</button>
<button onClick={handle1}>Filter by id 1</button>
<button onClick={handle2}>Filter by id 2</button>
{renderdata()}
</div>
);
}
Here i want to render the AlertSound component when value of bitcoin state changes but it only render when i refresh application and not when value of state is changed. value of bitcoin state is getting update but the AlertSound component renders only once when app is reloaded
App.js:-
const App = () => {
const [bitcoin, setBitcoin] = useState();
const myRef = useRef("");
const fetchDetail = async () => {
const { data } = await Axios.get(
"https://api.coincap.io/v2/assets/bitcoin"
);
setBitcoin(Math.floor(data.data.priceUsd));
};
useEffect(() => {
const interval = setInterval(() => {
fetchDetail();
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div>
<h1>{bitcoin}</h1>
{bitcoin < 41405 ? <AlertSound /> : " "}
</div>
);
};
export default App;
AlertSound.js:-
const AlertSound = () => {
const myRef = useRef(null);
return (
<div>
<audio ref={myRef} src={SoundFile} autoPlay />
</div>
);
};
Add dependency of bitcoin state in useEffect
const App = () =>{
const [bitcoin, setBitcoin] = useState();
const myRef = useRef("");
const fetchDetail = async () =>{
const {data} = await Axios.get("https://api.coincap.io/v2/assets/bitcoin");
setBitcoin(Math.floor(data.data.priceUsd));
}
useEffect(() => {
const interval = setInterval(() => {
fetchDetail();
}, 1000);
return () => clearInterval(interval);
}, [bitcoin]);
return(
<div>
<h1>{bitcoin}</h1>
{bitcoin < 41405 ? <AlertSound/> : " "}
</div>
)
}
export default App;
useEffect(() => {
debugger;
}, [filter]);
// eslint-disable-next-line
useEffect(async () => {
if (parseInt(localStorage.getItem("lastFetchTime")) + 8640000 > Date.now()) {
setRecipeList(JSON.parse(localStorage.getItem("recipeList")));
setIsLoading(false);
} else {
await fetch('https://api.spoonacular.com/recipes/random?number=20&apiKey=3c6b5aedfaf34bb899d1751ea2feb1b2')
.then((resp) => resp.json())
.then((data) => {
setRecipeList(data.recipes);
setIsLoading(false);
localStorage.setItem("recipeList", JSON.stringify(data.recipes));
localStorage.setItem("lastFetchTime", Date.now());
})
}
}, []);
I have these 2 useEffect in my program, the first one, with the listener is not being called even if the filter is changed. But it works if I remove the [] from the 2nd useEffect and the 2nd one runs on loop so I cant use it like that. I saw multiple forums, all of which suggests this should work.
import { useState, useEffect } from "react";
import { render } from "react-dom";
const sleep = (ms: number) => new Promise(
resolve => setTimeout(() => resolve('Resolved'), ms));
function App() {
const [filter, setFilter] = useState({ count: 0 });
const [get, set] = useState(0);
useEffect(() => {
console.log('Here');
}, [filter]);
useEffect(() => {
async function myFunction() {
const res = await sleep(5000)
.then(res => console.log(res));
setFilter({ ...filter, count: filter.count + 1 });
}
myFunction();
}, [get]);
return (
<div>
<p>App {get}</p>
<button onClick={() => set((get: number) => get + 1)}>
Click
</button>
</div>
);
}
render(<App />, document.getElementById("root"));
This minor snippet to be working for me as expected.
useEffect cannot be async. If you want to call an async function in useEffect() you need to do it like this:
EDIT: this is the complete useEffect
useEffect(() => {
async function getData() {
if (
parseInt(localStorage.getItem("lastFetchTime")) + 8640000 >
Date.now()
) {
setRecipeList(JSON.parse(localStorage.getItem("recipeList")));
setIsLoading(false);
} else {
const res = await fetch(
"https://api.spoonacular.com/recipes/random?number=20&apiKey=3c6b5aedfaf34bb899d1751ea2feb1b2"
);
const data = await res.json();
setRecipeList(data.recipes);
setIsLoading(false);
localStorage.setItem("recipeList", JSON.stringify(data.recipes));
localStorage.setItem("lastFetchTime", Date.now());
}
}
getData();
}, []);
I tested it and it worked as expected (I console.log() in the other useEffect())
There's nothing wrong with the useEffect. It's a bullet proof. But you make sure the following things:
Is filter updated during the component did mount?
The debugger will show up if you have open developer tool.
Isfilter updated during the component did update?
The debugger won't show up.
To make sure whenfilter is updated, use another effect hook but this time without dependency array.
useEffect(()=>{
console.log(filter) // analyze in the console
})
And if the value is updated during the update then you don't need to use dependency array but check the changes inside the effect hook by using some state for that as filter is coming from the update (props).
import { useState, useEffect, useCallback } from "react";
function App() {
const [isLoading, setIsLoading] = useState(false);
const [filter, setRecipeList] = useState({});
useEffect(() => {
// debugger;
}, [filter]);
// eslint-disable-next-line
const fetchData = useCallback(async () => {
if (
parseInt(localStorage.getItem("lastFetchTime")) + 8640000 >
Date.now()
) {
setRecipeList(JSON.parse(localStorage.getItem("recipeList")));
setIsLoading(false);
} else {
const data = await fetch(
"https://api.spoonacular.com/recipes/random?number=20&apiKey=3c6b5aedfaf34bb899d1751ea2feb1b2"
).then((resp) => resp.json());
setRecipeList(data.recipes);
setIsLoading(false);
localStorage.setItem("recipeList", JSON.stringify(data.recipes));
localStorage.setItem("lastFetchTime", Date.now());
}
}, []);
useEffect(() => {
setIsLoading(true);
fetchData();
}, [fetchData]);
return (
<div>
<span>{isLoading ? "loading" : "loaded!"}</span>
{!isLoading && filter && <div>filter size:{filter.length}</div>}
</div>
);
}
export default App;
I think it will work properly.
Thanks.
I am trying to let the FlatList get 20 posts from Firestore and render 20. when the end is reached I would like to call the getPosts method to get the next 20 posts which means I will have to have a way to save the last known cursor. This is what I was trying to do when converting class component to hooks.
Please can someone help me , no one answered my last question about this
const Posts = (props) => {
//How to get 20 posts from firebase and then render 20 more when the end is reached
const [allPosts, setAllPosts] = useState();
const [loading, setLoading] = useState(true)
const [isRefreshing, setRefreshing] = useState(false);
useEffect(() => {
getPosts();
}, []);
const getPosts = async () => {
try {
var all = [];
const unsubscribe = await firebase
.firestore()
.collection("Posts")
.orderBy("timestamp",'desc')
.get()
.then((querySnapshot) => {
querySnapshot.docs.forEach((doc) => {
all.push(doc.data());
});
setLoading(false);
});
setAllPosts(all);
if(currentUser === null){
unsubscribe()
}
} catch (err) {
setLoading(false);
}
};
const onRefresh = useCallback(() => {
setRefreshing(true);
getPosts()
.then(() => {
setRefreshing(false);
})
.catch((error) => {
setRefreshing(false); // false isRefreshing flag for disable pull to refresh
Alert.alert("An error occured", "Please try again later");
});
}, []);
return (
<FlatList
data={allRecipes}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
onRefresh={onRefresh}
/>
}
initialNumToRender={20}
keyExtractor={(item, index) => item.postId}
renderItem={renderItem}
/>
);
}
const Posts = () =>{
const [posts, setPosts] = useState();
const [data, setData] = useState();
const addPosts = posts => {
setData({...data,...posts})
// `setData` is async , use posts directly
setPosts(Object.values(posts).sort((a, b) => a.timestamp < b.timestamp))
};
}
You need to add a scroll event listener here
something like:
const Posts = (props) => {
useEffect(() => {
window.addEventListener('scroll', () => {
if (window.scrollY >= (document.body.offsetHeight + window.innerHeight)) {
// fetch more posts here
}
});
});
// ...rest of the codes
}