Apollo GraphQL useQuery re-fetching when history.replace method is called - javascript

I have a page that utilizes the Apollo GraphQL useQuery hook to fetch data from several different sources. Some of that data is displayed on the page in a list, and some gets populated into select menus that the user can change in order to filter data on the page. I'm also using a simple React hook for persisting the users selections via the history.location.state object (specifically using React Router Dom). The hook is essentially this:
history.replace({
...history.location,
state: {
...history.location.state,
[key]: value,
},
});
The issue I'm running into is that whenever history.replace gets called all of the queries re-fetch data, even if nothing has changed and it should serve a cached result. I've been struggling to find any information around when and why the Apollo client decides to re-fetch and if there's anything I can do to avoid this – will Apollo by default respond to any changes to the history object and cause a re-fetch?

Related

How to share data between components in angular

I use observable to send a value from one component to another (data is sent to the subscriber after clicking on this other component, i.e. via subject) I subscribe in another component and everything works fine until I refresh the page, after refreshing the page the component is recreated and after recreation the subscriber has no data as he did not go through the first component.How can I solve the problem?
I tried using rxjs operators as shareReplay but it didn't work like shareReplay
As your Angular app is destroyed and rebuilt when the page is refreshed, unfortunately you will lose all user state that is not saved somewhere. This is a common problem in building UIs so there are a number of tools available to combat this
Strategy:
Store your user state when an important change is made. This is called persisting state
Fetch and reapply your saved state on reload. This is called hydrating state
Options:
Persist to local storage and check for local storage values on reload to hydrate with
Persist within the users URL (simple values only), e.g. modifying the URL in some way which can be checked on reload. Assuming you are dealing with a single page, query parameters or fragments may be the way to go
Persist to a database via a POST/PATCH call and perform a GET request on reload to check for values to hydrate with
None of these methods are inbuilt into an RxJS operator (as far as I know) but we can easily leverage RxJS to achieve any of the above strategies with little effort. The tap operator is often used specifically to handle side effects, i.e. operations which should happen as a coincidence of an RxJS emission. That is precisely what we want here, in simple terms:
"If the subject emits a value, also trigger an operation which
persists the user state"
"On page load, check for any available saved user state and emit via the
relevant subject, hydrating the observables which the components will consume"
See example implementation below
tab.service.ts
type TabType = 'first' | 'second'
#Injectable({
providedIn: 'root'
})
export class TabService {
tabSelectedSubject: BehaviorSubject<TabType> = new BehaviorSubject<TabType>('first')
tabSelected$: Observable<TabType> =
this.tabSelectedSubject
.pipe(
tap(tab: TabType) => {
// ... your persist code here
this.saveTab()
},
distinctUntilChanged()
)
constructor() {
// ... your hydrate code here
this.fetchAndApplyTab();
}
saveTab(): void {
localStorage.setItem('tab', tab)
}
fetchAndApplyTab(): void {
const savedTab: TabType | null = localStorage.getItem('tab');
if (savedTab) {
this.tabSelectedSubject.next(savedTab)
}
}
}
In this case, we are exploiting the fact that our service is:
A singleton, so only loaded once per app (i.e. provided in the app root)
The service will be instantiated in the first component that loads which also injects it
This allows us to put our fetchAndApplyTab() logic in tab.service.ts's constructor and keep the code self-contained. However, depending on your use case, you may instead want to run fetchAndApplyTab() from your component manually itself.
This is happening because everything is in memory, and on page refresh all is lost, due the fact that angular app is re-initializing. You need to persist the state, for example write it into local storage, for this you could use "tap" operator from rxjs. And also in loading you could read data from localstorage end emit-it, for this you could use app_initializer hook.
there are 2 days majority to pass data between components
If both components are interconnected it means the parent or child
relationships then you can pass data with input-output decorators.
you can use the common service to share data between 2 components.
In SPA application if you refresh the browser then all in memory objects and observables are not present you need to again go back to the screen where it will be initialize.

Refresh NextJS query parameters on SSR React app

I have Server Side Render set up, so every time an user visit a page, for example
mypage?query=one
nextjs will cache that value for all the user, which is of course wrong.
So even if the user will have
mypage?query=two
still nextjs router.query function will return the value one while asPath is correct (showing the current URL).
How can I, in a class component, update the cached version with the current query? Which lifecycle is more opportune to use?

When to fetch data from API via Vuex again to keep the data up to date

I'm fetching data from an API and want to achieve it via Vuex. So my users store module is holding and managing all users.
To fetch all users I have a fetch action which calls the API via Axios and passes the users array from the response to the mutation.
I have a view component at /users which renders a list of users. Within that component I call
async mounted() {
await this.fetch();
}
to initialize the store. After that, the store getter will return the users array and the list gets rendered. I know that I can also fetch one item by id via getter
getById: state => id => state.users.find(user => user.id === id)
When navigating to a user detail page /users/:id I can make use of it, extract the userId via this.$router.currentRoute.params.id and render the user details. But what if I load that route directly? Then the store initialized with an empty users array and it won't find a single user.
So should I fetch all users again in the mounted hook of that user details view? I think calling the axios service directly would be an anti pattern because the resources say I should use Vuex as an interface between the component and the API.
Is there a best practise when to fetch all users from the api? Because let's image you navigate to another page which loads all users in a select component, should I fetch them from the getter? Maybe it will load a user that doesn't exist anymore. But polling all users every X seconds would be bad too...
What is the correct way to update the store data to make it accessible for all components in the entire application?
I don't agree that Vuex should be used to store an entire tables worth of user data in the manner you described. This sounds like a security concern to me. Remember that Vuex is a client-side store; meaning all your user data would be visible to any client using your application. You should never store sensitive data client-side.
Type this into your browser console, and you'll see what I mean.
document.getElementsByTagName('a')[0].__vue__.$store.state
Vuex should be used for storing non-sensitive data of an individual user required to drive their session.
If you are storing all user data for the purpose of listing accounts so that they can be managed by an admin or something, then I would instead, make that API call on demand when you load whatever view you have which lists your user accounts. This way you will always have the most up-to-date data, without the need for constantly polling your API.
I would paginate that data, loading only the first 5 records (to keep the API call initially light), then provide your admins the ability to search for accounts; that searching should be done server side, through your API.
Example:
methods: {
fetchUsers(limit, page, search) {
const thisIns = this;
if (search) {
this.userQuery = `/users/find?search=${search}`
} else {
this.userQuery = `/users/find?limit=${limit}&page=${page}`
}
thisIns.$http.get(this.userQuery)
.then(async (response) => {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
});
}
},
created() {
this.fetchUsers(5, 1)
},
You can then call your fetchUsers() method as needed, such as when performing a search for user(s).
Since you are using Axios, you can also consider implementing a caching mechanism, axios-cache-adapter, so that your API calls are cached for some period of time you define. This would reduce the number of API calls hitting your server.
I also don't consider this to be scaleable. Remember that an application user will have to wait for this payload when they first load your app. This also has performance implications; as your user data grows over time, you'll be consuming more and more of the devices RAM.

React+Javascript:-Component will update is getting called too many times causing my application to crash

componentWillUpdate() {
//Axios GET method for getting all the trades
//Adding data of db into mobx store/or syncing data of db and store
axios.get(`http://localhost:8091/trade`)
.then(res => {
this.props.store.arr = res.data;
})
}
This piece of code causing my browser to crash, my laptop to not responding.
Actually, whenever i was trying to delete the row of table containing trade by click of button then the trade was deleted but it took the need of refresh to see that the trade is deleted.
This was because my mobx store and db were not in sync.So as soon i refresh the (REST api) controller updates data in my mobx store.After this i can see that trade is deleted.
So in order to remove the need of refresh i thought to use component will update method.Within that method i tried to sync mobx store with controller data (db data).It worked but it caused the browser to take more than 2.5 gb of memory & at this point all the running applications starts getting crashed also.
So what is the good way to achieve the desired result?
Note i don't know why component will update is getting called too many times.
But i can verify the it because i can see the selection statements(of database) in spring (my server which is sending data to controller ).
Putting the above code inside component did mount is not removing the need of refresh but it is not causing the browser to crash also.
You should not be doing this type of operation in this lifecycle hook. You should use componentDidMount instead for any remote calls that need to happen. However since you are using mobx, you really should not be having these problems as they handle these type of problems for you with the observer pattern. Please read: https://mobx.js.org/getting-started.html to get up to speed and you should have no issues at that point.
Something in your component's props or state is causing it to update often, which is causing a lot of calls to the api. Your best bet would be to find out what is causing those updates and use nextState and nextProps arguments supplied to componentWillUpdate to check and send an api call only when needed. Something like:
componentWillUpdate(nextProps,nextState) {
if (nextProps.needToGetApi !== this.props.needToGetApi) {
//Axios GET here, so that unrelated prop/state change does not cause this to run
}
}
Hint: Add a breakpoint in componentWillUpdate and see what props or state mutations are happening on each call.

Should I use redux dispatch during initial rendering?

I'm trying to build a E-Commerce Store and it requires that I initially load a list of trending products on the home page.
Here, I can simply do without redux and simply display the data (roughly) of this sort
const trendingProducts = await get('/api/trendingProducts')
render(){
<TrendingProducts data={this.trendingProducts.data} />
}
I am using redux in my application. So should I do a dispatch elsewhere to fetch the trending products ?
All in all, Should I always handle every single fetch / render using only Redux dispatch ?
Redux is a store management for your application's state. Redux's dispatch is used to dispatching actions that aims to update your application's state in some way.
Hence if your application logic requires displaying information that belongs to your application's state - you need to take it from Redux store. If such information is not yet available into Redux store - you need to obtain it from some source (e.g. fetch) and use dispatch to update your application's state. If information, you're trying to display is not part of your application's state - you can display it directly, but in this case you'll need to handle React lifecycle events by yourself too since React re-draws components upon component's state change.
UPDATE: Your example code will work fine if you'll put your trendingProducts into component's state:
class MyComponent {
constructor() {
super();
this.state = {
trendingProducts: {}
}
}
componentWillMount() {
fetch('/api/trendingProducts').then(data => this.setState({trendingProducts: data}));
}
render() {
return (
<TrendingProducts data={this.state.trendingProducts}/>
)
}
}
That is very subjective and there is no correct answer but I can tell you by my experience.
Always keep your business logic separate from your component
If you're making API calls then you should definitely dispatch an action rather writing this in your component because there are lot of redux-like stores are emerging and it might happen that later you want to change your store using some different architecture. Your view layer should be independent on your API calls (business logic). In this way, while refactoring your app again, you'll just have to change the logic and your view will remain the same.
I'm writing this on my experience where we started refactoring the whole app from backbone to React. We had models and collections, we changed the whole html but we didn't change any business logic initially, just the views were deprecated but later we removed the business logic too using redux (iteratively). You should write your code in such a way that it has maximum reusability after all that's what React is and that's how you should write your front-end code.
Also, the component's state can reside in component where the whole app doesn't get affected. e.g. showing or hiding of a third-pane or overlay etc.

Categories

Resources