I have following code
const {
isLoading,
error,
data: calculatorProducts
} = useQuery('fetchSomething', ()=> fetchFunc())
This function should be called when I visit my main page, but when returning to this route from another page this request is not triggered and I don't get fresh data. What I want is this query to be called every time like useEffect
data from the cache is always returned if there is data in the cache. Additionally, react-query will update stale queries in the background on certain occasions, e.g. when a component mounts. This technique is called stale-while-revalidate.
All queries are instantly stale per default, so if you go to a route from a different page, you should get data from the cache and still see a request in the devtools. If not, then maybe your component isn’t really mounting?
If you want data to be removed from the cache when all components that use this data unmount, you can set cacheTime: 0. But it means you’ll get a hard loading with a spinner when you come back, which is what react-query is trying to avoid
As for now, initial data are cached so you also have to set:
initialData: undefined
This made my data fresh (it takes undefined as cached initial data, so each time it takes fresh data because cache is "empty")
Related
I have a redux store containing multiple states which is populated by same api but multiple end-points.
I have a react app with multiple components, which access to one of these states, and they all call to the correspondent api no mount by dispatching a async function (redux-thunk) to the store.
In the App.js multiple components are used.
Problem:
When all the components mount they call to the api concurrently and exceeds the maximum number of call allowed for the short time frame. Thus all calls reply with status code 429: Too many request.
*Extra notes: I'm buliding a news web-app, and using gnews.io api with multiple search queries and end-points to get data.
In your case i believe one approach would be to create a request type and use it in your state instead of the raw value returned from the endpoint, something like:
type TApiRequestState<T> = {pending: boolean, response: T}
When you dispatch an action that will trigger the endpoint in thunks you would set the api state to loading.
After that all you need to do is check if the request is not already running on the other components.
if (!value.pending && !value.response) {
dispatch(thunksFn)
}
In theory you could dispatch all of the initial requests on a parent component and only render these child components when the initial requests are done. But this depends on a project to project basis IMO.
I am retrieving data from a booking api where the availability of rooms changes every time. What I am doing is retrieving the data every 1 second using setInterval.
const interval = setInterval(getData, 1000);
which works fine, but I thought it would be better if there was a way to only retrieve if there was an update or change to the data api I was retrieving. I've looked everywhere but I can't find anything about this .
For the server side, say I fetch data from https://api.publicapis.org/entries using Express and make some changes to the data and now my data is available from https://localhost:3001/api/entries and I consume it and display what I want using React.
Let's say that https://api.publicapis.org/entries changes its data every time, how can I recover when only the data changes, so that in https://localhost:3001/api/entries the data changes accordingly.
I recommend you to use QueryCache mechanism from React Query (https://react-query.tanstack.com/reference/QueryCache#_top), it's very powerful feature because by default every query result is cached for five minutes and relies on that cache for subsequent requests.
React query knows that the server data might have updated and the cache might not contain the latest data so a background refetch is triggered for
the same query and if the fetch is successful the new data is updated in the UI. Since our data is the same as the cached data we don't see any change in the UI...
YouTube video tutorial as a reference
I think you can do something from both server-side and client-side, fro client-side, use setTimeout instead of setInterval, that can let you send next request only if get the last response, for example:
let timeout
function getData() {
// await request here..
timeout = setTimeout(getData, 1000)
}
and then, query new data with the last update data to the server, so that the server-side could know if there any update data need to send to you. If you want the server notices you when should you get the update, I think you must use sockets.
So imagine I have these 2 URLs:
example.com/Profile
example.com/Profile/City
When example.com/Profile is visited, I present component Profile and when example.com/Profile/City is visited, I present component City.
I need to make a GET request that returns information used in both components. Currently, I make that GET request in the mounted() lifecycle hook of both components but I have been wondering if I can do it once only in the parent Profile component and then just props it down to City so that I could get rid of 1 GET request.
However, I am not sure what would happen if a user straight up types example.com/Profile/City? Considering the GET request is executed on mounted() of the Profile component and we haven't mounted Profile since the user straight up typed the URL that leads to City component , will I have the response in the child component?
Considering the GET request is executed on mounted() of the Profile component and we haven't mounted Profile since the user straight up typed the URL that leads to City component , will I have the response in the child component?
No, you won't. You will end up with no data.
I have been wondering if I can do it once only in the parent Profile component and then just props it down to City so that I could get rid of 1 GET request.
Not really. However if you used a global state management solution like vuex, you could move the request and the data into this global state and trigger the vuex action for the request from both component's mounted.
Then you could implement logic in your vuex action that would skip the request if the data is already present (i.e. from having previously visited /profile). But in that case you should make sure to save some timestamp so you don't cache the data indefinitely.
If you expect the data to change frequently, I wouldn't cache it like that at all and rather do a duplicate request everytime to ensure the data is a fresh as possible.
I have an isomorphic react application using redux,react-router, react-router-redux, I want to bind my components with url query params and when the query params changed, send request to an API and update my state and finally fetch data.
I know with react-router-redux it automatically update my state.
Also I want to show a pre-loader (spinner) to user when data is fetching.
what is the best way to write an action creator for this issue ? thanks
I want to show a pre-loader (spinner) to user when data is fetching
Pass a data prop to your component. When it's undefined, show a spinner.
const MyComponent = (props) => {
if (!props.data) return <Spinner />
...
}
When the query params changed, send request to an API and update my state and finally fetch data
The most simple solution (this can get quite complex depending on your needs) is to fire this logic on the Component's lifecycle hooks.
When the route changes react-router, with a typical configuration, will cause the component to render if you were previously on a different route, and update if you were previously on the same route (i.e. the same path, but with different query params).
So for the first case, use componentDidMount, for the second case, use comonentDidUpdate(prevProps, prevState) lifecycle hook.
To map the asynchronous API call to redux action(s), use a library like redux-thunk or probably more tailored to this use case, redux-promise-middleware. How to use those is a bit deep in scope for me to go into here, but they both have excellent documentation.
In the end, you'll want to map those actions in a reducer put the response data somewhere in your state tree, then using react-redux bind that location in the state tree to the data prop on your Component, as I mentioned before.
I am using React with react-router and Reflux as my datastore, but I am unsure on how to best deal with persistence to allow page refresh.
My components connect to the store with Reflux.connect, but since the store fetches the data from a backend, it is not available yet when the Components first initialize and render.
When the user enters my app from the start, then all this data is loaded in order and available when it needs to be, but if further down a route you trigger a page refresh, react tries to render components that rely on data that is not there yet.
I solved this by constantly keeping a copy of data in LocalStorage and serving that from the Reflux store getInitialState(), so that all components get the data before they render.
I wonder if this is the proper way to do it. When for some reason the local storage data gets cleared or corrupted, the interface goes blank, because the components cannot access the correct data. Substructures and properties don't exist and trigger javascript errors. It seems like a messy and unreliable solution.
I am curious to know what patterns are used to solve this.
------ edit -----
To answer to the comment of WiredPrairie:
1) Why are you initializing components with data in getInitialState?
When my components use Reflux.connect, they don't have the data in their state yet on the first render as the store still needs to fetch its data. My views currently don't work gracefully with undefined data. By returning the locally stored cache from the Reflux store in getInitialState(), all connected components will get that data before their first render call.
2) What's causing a page refresh and why can't the data be loaded in the same manner as it was the first time?
It's mainly a workaround I had to build around livereload refreshing the page when I make edits (will look into using react-hotloader later but is not an options yet), but users can also just hit refresh when they are somewhere in my nested views and that would have the same effect. When they manually refresh, they are not entering the app at the start.
3) When components are wired to the change events of a store, why don't they update then?
They do update, but like I said they don't deal with empty data right now and on first render they will miss it waiting for the store to fetch things. I can make all my views work gracefully with empty data, but that would add a lot of boilerplate code.
From the replies so far, I get the feeling that what I'm doing with localStorage is the common way to do it. Cache stuff locally in localStorage or sessionStorage or something similar, and serve that data immediately after a refresh.
I should make my views a bit more robust by gracefully handing empty data on the odd occasion that localStorage doesn't work properly.
I've been caching each Store in sessionStorage when its emitChange() fires, and initializing the store from sessionStorage if cached data exists, or null values otherwise. This seems to work provided that the views can handle null values, which is probably a good idea anyway (it sounds like this is your main problem).
I'd suggest making your views handle the case of no data gracefully, initialize everything with null values if the cache isn't available, and then call the backend to update the null values with an Action whenever the data returns.
I haven't tried Reflux, but in regular Flux it would look like this (maybe you can apply the same principle):
var _data;
if (sessionStorage.PostStore)
_data = JSON.parse(sessionStorage.PostStore);
else {
_data = {
posts: null
};
BackendAPI.getPosts(function(err, posts) {
if (posts) {
PostActions.setPosts(posts);
}
});
}
...
AppDispatcher.register(function(payload) {
var action = payload.action;
switch (action.actionType) {
...
case Constants.SET_POSTS:
_data.posts= action.data.posts;
break;
default:
return true
}
// Update cache because state changed
sessionStorage.PostStore = JSON.stringify(_data);
PostStore.emitChange();
return true;
});