What I am trying to do:
useMutation to edit the email value and re render the page. The data changes on refresh but the cache needs to change as well and I am having trouble modifying the cache.
Legwork I have read/looked over before posting here:
Documentation on Mutations
Documentation on Updating cache
Obtaining an Object's Custom ID
What I think the problem is:
the GraphQL database I am using does not implement a proper ID and I can not change that. I will have to modify the ROOT_QUERY cache and update it the hard way. As my code is right now It's reading the ID I want to use (email) as undefined. The error message simply reads as "Unhandled Rejection (Error): Cannot read property 'email' of undefined" pointing to functions in the node modules and nowhere near my function.
My component's code can be found in full here, but I will point to where I think the smoking gun ultimately is.
I have tried checking as much as I can but have no way of telling if i am reaching things correctly. I have looked at other stack posts, youtube, and tried reaching out to a few discord channels only to hit a wall. Any help and explanation would be great.
Figured out the problem was cache.identify was not needed at all! I just had to fix the InMemoryCahce at the index.tsx
const client = new ApolloClient({
uri: "http://localhost:8080/graphql",
cache: new InMemoryCache({
typePolicies: {
People: {
keyFields: ["email"],
},
},
}),
});
This made things easier on getting the right index.
Related
I'm interested in opinions on how to manage global client state (Zustand) invalidation as a result of a socket update which modifies the network state cache (Apollo).
Let's say we have the following state:
Apollo cache (requested from server):
{ posts: [
{ id: 1, text: 'Hello World!' },
{ id: 2, text: 'I has a bucket!' },
{ id: 3, text: 'Thanks StackOverflow!' }
] }
Zustand store (client state):
{
selectedPost: 2,
editMode: true
}
In this state, we show a UI which allows editing the contents of post 2. We run into an edge case however, if another user deletes post 2. A socket update will remove that post from our network cache, while our client state still believes it is selected and being edited.
This could result in a strange user experience where the post has disappeared, but they still see the edit UI, and any attempt to save will fail, since the resource no longer exists to be updated on the backend.
The expectation is to set the following client state if the selected feedback is removed:
{
selectedPost: null,
editMode: false,
}
The full situation is more complex, and I'd want to show a message to the user, but I feel those details are unimportant to this discussion, so I've minimized the state as much as possible.
I've considered a few options for solving this problem. Each with some pros and cons. Any one of these can work. What I'm interested in is advice from someone familiar with this type of problem, on which solution is most viable long-term.
useEffect
Write a useEffect which listens for posts not including a post with post.id === selectedPost, and updates client state to { selectedPost: null, editMode: false }.
This has the disadvantage that there is an in-between render where the post does not exist, but it is still selected and we still show the edit UI. Meaning we have to be sure our components can handle that invalid state. It could also result in a visual flicker where the user sees this invalid UI briefly before the client state is adjusted.
Side-effect of Apollo cache change
I'm not sure if this is something that can be done.
Write a callback which runs when the Apollo cache changes, but before the resulting render occurs. This callback checks if selectedPost exists in the new cache. If not, it updates the client state. This would not cause the in-between invalid render.
I haven't seen anything on the Apollo docs about this. So I'm not sure if it's possible.
Update client state in the socket handler, at the same time Apollo cache is updated
We have functions that handle each socket event, which make the required modifications to the Apollo cache. We could add the client state checks and updates here. This would avoid the invalid render problem.
The downside is we would have to manually handle every socket event that could result in removing a post. Which would result in duplicated code and room for error if new socket events were introduced.
Does anyone have experience with updating client state as a side-effect of network state like this? Any suggestions on how to handle it elegantly without the invalid in-between render?
I would love a short code review of "my" implementation of optimistic ui patterns. I'm using SWR, immer and a custom fetch hook to do most of the heavy lifting. However, I'm not really sure if this is indeed the way to do it. Especially when it comes to asigning a temporary id to the optimistically generated item. Shouldn't I clear it somewhere? May it cause issues?
const spawn = async flavour => {
const payload = {
planId: flavour.id,
name: 'test',
description: '',
}
mutate(
'/account/instances',
produce(draft => {
draft.push({
...payload,
id: uuid(),
plan: {name: flavour.name},
state: {status: 'PENDING'},
image: {name: flavour.image.name},
})
}),
false
)
mutate(
'/account/instances',
await doFetch('/account/instances', 'post', payload)
)
}
Thanks!
Your code seems correct to me. It will update optimistically the UI, fetch data from the backend, and update again the UI with that data if it differs.
Regarding the generated id, it really depends on what you do with it. If you do nothing important it will probably be ok as that id will be overwritten with the real one when the backend replies. But it may cause problems if you display it to the user (the user will see it being updated), or even worse if you provide an action based on it.
I would also like to draw your attention on the fact that the reply may fail for network reasons, or reasons related to a problem on backend side. In that case, if you receive just an error or never receive any reply, your UI will remain in an incorrect state until the next successful fetch made by swr.
To avoir this kind of problem, you may be interested in use-mutation. It's a small lib designed to be used with swr to rollback the optimistically updated data in case of error.
I am new to both React-JS and Oboe.js. I am trying to speed up loading of some JSON data by using Oboe to stream the results. Unfortunately I am unable to do an update state in the function block. So I try to call another function that does the stateSet. Below is a method I have tried but doesn't work. It errors out a mapping function that uses search-results to render it in a table.
var that = this;
oboe({
url: //url,
method: 'POST', // optional
body: //POST-DATA, // optional
})
.on('node', '*', function(things){
that.updateState(things);
// This callback will be called everytime a new object is
// found in the foods array.
console.log( 'Go eat some', things.id);
});
updateState = (props) => {
this.setState({search-result: props});
}
What I am not sure about is the right way of updating a state with oboe.js and React?
Is there a better library to use for streaming JSON data into React?
Recommended approach
If you have the ability to change things server-side, then I would not recommend using Oboe for this. Oboe is useful if your only alternative is to load a large JSON object and you would like to access that data before the whole thing can be parsed.
The best way to optimize loading a lot of data on a client is to send less data at a time and to make multiple requests. A web-socket is the best approach, and Socket.io is a good tool for doing that.
If you need to use Oboe
I'm working to put together an example of oboe.js + react for you to look at, though it's tricky as much of the activity of Oboe happens outside the React lifecyle. I'll update this answer with that example 👍
I am new on web development.. Sorry if the question sounds stupid or if I had mess up with my code.
I have this problem: I've tried to create a simple "To Do List" application using MERN stack.
I did connect React with the backend: I can send get and post request and they are working (i'm adding items to my mongoDB).
Here comes the issue: I cannot delete item from my list. The delete request is not working because I'm not able to get my item ID in react and honestly I don't know why.
Here it is my react code:
code
The problem should be that I'm not getting the ID of my data... But I don't know how to fix it
the problem is that instead of passing the id to deleteNow function, you are passing the parameters from the onPress event.
So, if you want to make it work, do this change:
change:
onPress={deleteNow} to onPress={() => deleteNow(data._id)}
Update onPress={() => deleteNow(data._id)} . if you not did that in SingleItem component
I am adding one more point.
your code
In addNote function -
1.update the state data with newItem received in the function.
2.then updated the Db.
now, the problem is the newItem added to data state won't have _id property because it is not from db.
my suggestion
1. post your data as you did with axios.
2. received the newly added db document via response and add to data state.
As a preface, I'm still new to React, so I'm still fumbling my way through things.
What I have is a component that fetches data to render an HTML table. So I call my Actions' fetchData() (which uses the browser's fetch() API) from within componentWillMount(), which also has a listener for a Store change. This all works well and good, and I'm able to retrieve and render data.
Now the next step. I want to be able to fetch new data when the component's props is updated. But I'm not exactly sure what the proper way to do so is. So I have a three part question
Would the proper place to do my fetchData() on new props be in componentWillReceiveProps(), after validating that the props did change, of course?
My API is rather slow, so it's entirely possible a new prop comes in while a fetch is still running. Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Related to the above question, is there a way to ensure only one fetch is running at any time besides having something like an isLoading boolean in my Action's state (or elsewhere)?
Yes, componentWillReceiveProps is the proper place to do that.
Regarding point 2 and 3:
The idea of cancelling the task and maintaining 'one fetch running' seems to be inadequate. I don't think this kind of solution should be used in any system because implementation would limit an efficiency of your app by design.
Is it possible to cancel the old fetch and start a new one, or at least implement logic to ignore the original result and wait for the results from the newer fetch?
Why don't you let a 'newer fetch' response override an 'old fetch' response?
If you really want to avoid displaying the old response you can implement it simply using a counter of all fetchData calls. You can implement it in this way:
var ApiClient = {
processing: 0,
fetchData: function(){
processing++
return yourLibForHTTPCall.get('http://endpoint').then(function (response)){
processing--
return response
}
},
isIdle: function(){
return processing == 0
}
}
and the place where you actually make a call:
apiClient.fetchData(function(response){
if(apiClient.isIdle()){
this.setState({
})
}
}
I hope yourLibForHTTPCall.get returns a Promise in your case.