RTK query send data, is coming back manipulated - reactjs

At work, I need to make an infinite scroll pagination with RTK-query. I am using Firestore as a DB, and I made a very basic version with just async functions super quickly. Now I need to convert what I have made to RTK query.
As I was doing this I noticed I was not longer able to fetch more data from Firestore because my query with Firestore was not responding. After doing some digging, I found out that RTK query is somehow changing my data. I will explain below with my code. This very well would be a Firestore problem as well.
async queryFn() {
try {
const { prod, lastDoc } = await fetchInitialData();
return { data: { prod, lastDoc } };
} catch (err) {
return { error: err };
}
This is how I am sending my data with RTK-query, I left a lot of code out since it is not needed. Down below is my react file, that calls my RTK query hook, as well as checks to see if the data being sent from RTK is correct
const [products, setProducts] = useState<BasicProductData[] | null>();
const [lastDocSaved, setLastDocSaved] = useState<any>();
const { data, isLoading, isError, error, isSuccess, refetch } = useFetchProductsQuery(null);
useEffect(() => {
getPost();
if (data && lastDocSaved) {
console.log(data.lastDoc === lastDocSaved);
}
}, []);
const getPost = async () => {
const { prod, lastDoc } = await fetchInitialData();
setProducts(prod);
setLastDocSaved(lastDoc);
};
As you can tell both of these ways of getting the initial data is pretty much exactly the same. Finally here is my pagination function, to fetch more data from firebase to keep the infinite scroll going.
const getMorePosts = async () => {
const postData = await fetchMoreData(lastDocSaved);
setLastDocSaved(postData.lastDoc);
setProducts([...products, ...postData.prod]);
};
I know that 'data' I get back from the RTK hook is not the same since I manually have checked if is by using that console.log in the useEffect. I also know it does not work since if I setLastDocSaved(data.lastDoc) the pagination does not work.
This is strange to me, since both data.lastDoc and const { lastDoc } = await fetchInitialData() come back as pretty much identical in the console, however lastDoc properly queries from firebase and data.lastDoc does not.

Related

RTK Query response state

I'm trying to convert some Axio code to RTK query and having some trouble. The 'data' response from RTK query doesn't seem to act like useState as I thought.
Original axio code:
const [ importantData, setImportantData ] = useState('');
useEffect(() => {
async function axiosCallToFetchData() {
const response = await axiosAPI.post('/endpoint', { payload });
const { importantData } = await response.data;
setImportantData(importantData);
}
axiosCallToFetchData()
.then((res) => res)
.catch((error) => console.log(error));
}, []);
const objectThatNeedsData.data = importantData;
New RTK Query code
const { data, isSuccess } = useGetImportantDataQuery({ payload });
if(isSuccess){
setImportantData(data.importantData);
}
const objectThatNeedsData.data = importantData;
This however is giving me an infinite render loop. Also if I try to treat the 'data' object as a state object and just throw it into my component as:
const objectThatNeedsData.data = data.importantData;
Then I get an undefined error because it's trying to load the importantData before it's completed. I feel like this should be a simple fix but I'm getting stuck. I've gone through the docs but most examples just use the if statement block to check the status. The API calls are being made atleast with RTK and getting proper responses. Any advice?
Your first problem is that you always call setImportantData during render, without checking if it is necessary - and that will always cause a rerender. If you want to do that you need to check if it is even necessary:
if(isSuccess && importantData != data.importantData){
setImportantData(data.importantData);
}
But as you noticed, that is actually not necessary - there is hardly ever any need to copy stuff into local state when you already have access to it in your component.
But if accessing data.importantData, you need to check if data is there in the first place - you forgot to check for isSuccess here.
if (isSuccess) {
objectThatNeedsData.data = data.importantData;
}
All that said, if objectThatNeedsData is not a new local variable that you are declaring during this render, you probably should not just modify that during the render in general.

useLazyQuery in Promise.all only storing last response in cache

I'm trying to use the same query with different variables using useLazyQuery. I built a hook for this reason and it fetches everything alright. However, it never really uses cached data. I looked into the cache and it's only storing the response from the last request in the array. This is a simplified version of my hook:
const useProductSearchQuery = (categories) => {
const [getProducts] = useLazyQuery(QUERY);
const [data, setData] = useState();
useEffect(() => {
async function getProducts() {
const responses = await Promise.all(
categories.children.map((cat) =>
getProducts({ variables: { category: cat.id } })
)
);
setData(responses);
}
getProducts();
}, [productCategories, getProducts]);
return { data };
};
I'm not sure if this use case fits useLazyQuery so maybe that's why it doesn't work. I just needed an imperative way of running my queries and this seemed easier than using a consumer to pass the client around.
The other alternative would be to just iterate categories and then have a useQuery hook for each but I'd prefer having my data ready in one go.
Nevermind I didn't notice the hooks docs mention the useApolloClient hook (https://www.apollographql.com/docs/react/api/react/hooks/#useapolloclient)... Works like a charm!

Why is the initial state getting printed along with the data?

I am fetching data from a backend api.
const Home = () => {
const [posts,setPosts]=useState([]);
useEffect(()=>{
const fetchPosts= async ()=>{
const res = await axios.get("/posts")
setPosts(res.data);
}
fetchPosts();
},[])
console.log(posts)
However in doing this, the initial state of posts which is [] is also getting printed.
Why is this happening and how can I avoid this and only get the data?
that is the reason why you see loading in real-world App because communication between server and client takes time so we show loading or show error if API gets error the same logic goes to component or element whatever your are trying to show as well
if(posts.length > 0) {
console.log(posts)
} else {
console.log("Loading...")
}

How to set data to the state after fetching from backend?

I want to get data from the backend and want to set those data to the state in ReactJS. Here is my source code
const [eachAsset, setEachAsset] = useState([]);
function ShowModalView(id)
{
axios.get("http://localhost:8070/assets/detail/"+id).then((res)=>{
const data = res.data
setEachAsset(data)
//console.log(eachAsset);
}).catch((err)=>{
console.log(err.message);
})
setShow2(true);
}
When I uncomment the console log, it shows an empty array. It means, setEachAsset(data) does not work properly. But I want to store data that are getting from the backend to the eachAsset state. What is the problem of this source code?
setEachAsset([...data])
I hope this would work
I would recommend using async-await which makes the code easier to read and understand the flow of the program as compared to promise chains.
const [eachAsset, setEachAsset] = useState([]);
const ShowModalView = async (id) => {
try {
const resp = await axios.get("http://localhost:8070/assets/detail/"+id);
setEachAsset(resp.data)
console.log(resp.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
setShow2(true);
}

react-query always return stale data and no call is made to server

I recently started using react-query and have encountered the issue that always stale data is returned and no call to server is made. here is the react query related code:
export function useGetAccount(id: number){
return useQuery([`account${id}`, id], async (args) => {
const [key, accountId] = args.queryKey
const [acc, teams, modules] = await Promise.all([
getAccount(),
getTeams(),
getModules()])
let account: AccountDetail = {
accountId: acc.accountId,
userId: acc.userId,
companyId: acc.companyId,
login: acc.login,
email: acc.email,
description: acc.description,
isActive: acc.isActive,
providers: acc.providers,
teams: teams,
modules: modules
}
return account
async function getAccount() {
const api = createApi() // <= axios wrapper
const { data } = await api.get(`accounts/${accountId}`, undefined, undefined)
return data as AccountModel
}
async function getTeams() {
const api = createApi()
const { data } = await api.get(`accounts/${accountId}/teams`, undefined, undefined)
const { collection } = data as ResponseCollectionType<AccountTeam>
return collection
}
async function getModules() {
const api = createApi()
const { data } = await api.get(`accounts/${accountId}/resources`, undefined, undefined)
const { collection } = data as ResponseCollectionType<ModuleAccessModel>
return collection
}
})
}
I even reduced the cache time but still to no avail. I do not see any calls made to server side except after a long delay or if I open the browser in incognito mode then first time the data is fetched and then no call is made.
this is used in a component which shows the details and is passed the id as a prop. everything is working fine except that the data is the one which was retrieved first time and even a refresh (F5) returns the stale data.
what changes do I need to make in this case?
[observation]: Ok, it does make a call but only after exact 5 minutes.
well the problem is not in react-query but in axios, described here Using JavaScript Axios/Fetch. Can you disable browser cache?
I used the same solution i.e. appending timestamp to the requests made by axios and everything worked fine.

Resources