Why .then() which is in useEffect is not updating my state - javascript

targetMovie is null when it comes to rednering. I couldn't find any solutions. First time having unsolvable problem. Please help!
async function getMovie(id) {
try {
const res = await axios.get(apiEndPoint + "/" + id);
const movies = await res.data;
return movies;
} catch (err) {
console.log(err);
}
}
const MovieEdit = () => {
const { id } = useParams();
const [targetMovie, setTargetMovie] = useState(null);
useEffect(() => {
getMovie(id)
.then((mov) => {
setTargetMovie(mov);
console.log(mov);
})
.catch((err) => console.log(err));
}, []);
console.log(targetMovie);
if (targetMovie) return <AddMovie movie={targetMovie} />;
return <Navigate to="/not-found" />;
};

You need to represent 3 states:
You're currently waiting on getMovie to complete
getMovie completed successfully
getMovie completed and returned null/undefined
You're currently using the same condition (!targetMovie) to represent both 1. and 3. which is why you're running into issues.
Try this:
const MovieEdit = () => {
const { id } = useParams();
const [isFetching, setIsFetching] = useState(true);
const [targetMovie, setTargetMovie] = useState(null);
useEffect(() => {
getMovie(id)
.then((mov) => {
setIsFetching(false);
setTargetMovie(mov);
console.log(mov);
})
.catch((err) => {
console.log(err));
setIsFetching(false);
}
}, []);
if (isFetching) return null;
if (targetMovie) return <AddMovie movie={targetMovie} />;
return <Navigate to="/not-found" />;
};

Related

React Query return undefined data while in network tab the data exists but it does not show on the page

The data is coming from the backend and i can see it in the network tab, but react query is returning an undefined data, it's not even reacting with the data because it returns false loading and fetching in the console, and the devtools is set to fresh(1) . it was working before but now can't seem to figure out a solution .
This is my App.js:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
cacheTime: 0,
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient} contextSharing={true}>
<PrivateRoute
exact
path="/dashboard/snp/match"
component={MatchedSequences}
/>
<ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
);
}
export default App;
This is my component:
const queryClient = useQueryClient();
const [showQuery, setShowQuery] = useState(false);
const [showSequence, setShowSequence] = useState(false);
const { isLoading, isFetching, error, data, status } = useQuery(
"dn",
() => {
fetch("/api/users/find").then((res) => {
return res.json();
});
},
{ keepPreviousData: false },
);
console.log({ isLoading, isFetching });
console.log("error", error);
console.log({ data });
I'm using react Query version 3.34.
Your query function (the second parameter to useQuery) doesn't return anything:
() => {
fetch("/api/users/find").then((res) => {
const result = res.json();
console.log({result});
return result;
});
},
if you use curly brackets for the arrow function, you need the return keyword:
() => {
return fetch("/api/users/find").then((res) => {
const result = res.json();
console.log({result});
return result;
});
},
alternatively, you can use the implicit return from arrow function, but then you have to omit the curly brackets:
() => fetch("/api/users/find").then((res) => {
const result = res.json();
console.log({result});
return result;
});
please update your code to add logging inside of the fetch, like this:
const queryClient = useQueryClient();
const [showQuery, setShowQuery] = useState(false);
const [showSequence, setShowSequence] = useState(false);
const { isLoading, isFetching, error, data, status } = useQuery(
"dn",
() => {
fetch("/api/users/find").then((res) => {
const result = res.json();
console.log({result});
return result;
});
},
{ keepPreviousData: false },
);
console.log({ isLoading, isFetching });
console.log("error", error);
console.log({ data });
What value does result have?

Mapping through a fetched array from firebase but nothing shows up even though the array is not empty [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I fetched an array of products from firebase with the normal way :
export const getUsersProducts = async uid => {
const UsersProducts = []
await db.collection('products').where("userID", "==", uid).get().then(snapshot => {
snapshot.forEach(doc => UsersProducts.push(doc.data()))
})
return UsersProducts
}
and the fetched array shows up in the dom normally, but when I tried to fetch it with onSnapshot method it didnt show up on the DOM even though in appeared in my redux store and when I console log it, it shows up normally.
export const getUsersProducts = uid => {
let UsersProducts = []
db.collection('products').where("userID", "==", uid).onSnapshot(querySnapshot => {
querySnapshot.docChanges().forEach(change => {
if (change.type === "added") {
UsersProducts.push(change.doc.data())
}
})
})
return UsersProducts
}
here is the code used to show it in the DOM
const MyProducts = () => {
const CurrentUserInfos = useSelector(state => state.userReducer.currentUserInfos)
const searchQuery = useSelector(state => state.productsReducer.searchQuery)
const myProducts = useSelector(state => state.productsReducer.usersProducts)
const dispatch = useDispatch()
const settingUsersProductList = async () => {
try {
const usersProducts = getUsersProducts(CurrentUserInfos.userID)
dispatch(setUsersProducts(usersProducts))
console.log(myProducts)
} catch (err) {
console.log(err)
}
}
useEffect(() => {
settingUsersProductList()
}, [CurrentUserInfos])
return (
<div className="my-products">
<div className="my-products__search-bar">
<SearchBar />
</div>
<div className="my-products__list">
{
Object.keys(myProducts).length===0 ? (<Loading />) : (myProducts.filter(product => {
if(searchQuery==="")
return product
else if(product.title && product.title.toLowerCase().includes(searchQuery.toLowerCase()))
return product
}).map(product => {
return(
<ProductItem
key={product.id}
product={product}
/>
)
}))
}
</div>
</div>
)
}
export default MyProducts
You are returning the array before promise is resolved hence its empty. Try this:
export const getUsersProducts = async uid => {
const snapshot = await db.collection('products').where("userID", "==", uid).get()
const UsersProducts = snapshot.docs.map(doc => doc.data())
return UsersProducts
}
For onSnapshot, add the return statement inside of onSnapshot,
export const getUsersProducts = uid => {
let UsersProducts = []
return db.collection('products').where("userID", "==", uid).onSnapshot(querySnapshot => {
querySnapshot.docChanges().forEach(change => {
if (change.type === "added") {
UsersProducts.push(change.doc.data())
}
})
return UsersProducts
})
}

how to get updated data automatically after 10 sec using setinterval

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

How to update an array using useState Hook

I've tried to fetch data from a URL and get the result as JSON format, then store not of the object result in my state. but it always returns an empty array.
const [genres, setGenres] = useState([]);
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
});
};
getGenres();
}, []);
Here is the code:
I don't see where the problem can be.
ps: I deleted the import so the code is more readable
import React, { useEffect, useState } from "react";
function App() {
const [quoteOfTheDay, setQuoteOfTheDay] = useState("");
const [authorOfQod, setAuthorOfQod] = useState("");
useEffect(() => {
const getQuoteOfTheDay = async () => {
fetch("https://quotes.rest/qod?language=en")
.then((response) => response.json())
.then((data) => {
const qod = data.contents.quotes[0].quote;
const author = data.contents.quotes[0].author;
setQuoteOfTheDay(qod);
setAuthorOfQod(author);
});
};
getQuoteOfTheDay();
}, []);
const [genres, setGenres] = useState([]);
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
});
console.log(genres); // genres always empty
};
getGenres();
}, []);
return (
<div className="app">
<Head quoteOfTheDay={quoteOfTheDay} author={authorOfQod} />
<div className="app__category">
<QuoteCategory genre="sport" />
</div>
</div>
);
}
export default App;
Thank you so much
I think it should work if you change
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
to
setGenres((oldGenres) => [...oldGenres, ...data.genres]);
Are you sure that
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
setGenres(data.genres);
});
};
getGenres();
}, []);
is not enough? :)
Up. If you began you can use async-await syntax till the end. It looks more neatly.
useEffect(() => {
const getGenres = async () => {
const response = await fetch("https://quote-garden.herokuapp.com/api/v2/genres");
const { genres } = await response.json();
setGenres(genres);
};
getGenres();
}, []);
you should put genresState as your dependency
const [genresState, setGenres] = useState([])
useEffect(() => {
const getGenres = async () => {
const response = await fetch("https://quote-garden.herokuapp.com/api/v2/genres");
const { genres } = await response.json();
setGenres(genres);
};
getGenres();
}, [genresState]);

React hooks FlatList pagination

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
}

Categories

Resources