react-hooks query and setState causes 500 error - reactjs

I think this could be a bug or a misconfiguration.
I get this error:
networkError: ServerError: Response not successful: Received status code 500
Submitting the form the first time I would get the desired result, but if I hit the submit button again, I get the networkError message above.
const client = useApolloClient();
const [val, setValu] = useState({
email: 'example#example.com',
password: 'password',
texterror: {
status: false,
text: ''
},
})
//the submit function below:
const handleLogin = async (e) => {
e.preventDefault();
await client.query({
query: Handle_Login,
variables: {
email: val.email,
password: val.password
}
}).then((e) => {
console.log(e)
}).catch((e) => {
setValu({
texterror: {
status: true,
text: 'it be broke yo'
}
})
})
}
However, if I remove setValu({texterror: {status: true, text: 'it be broke yo'}}) in the catch, The 500 error goes away. I could spam the submit button and I wouldn't receive the 500 error.
For right now I'm going to not have a setState inside just to avoid this problem, but I would like to know if there is a solution to this issue.
Any help would be appreciated. Thank you in advance!

At the top level, it's clear that you're causing some kind of error on the server side. A 500 code means there is an "internal server error". So you should really look at your server code and figure out how to safeguard against unexpected input so that it won't error and/or crash but instead return code 400 which is "bad request".
So the question becomes, what about your frontend code is causing a malformed server request?
I suspect that you're trying to use setValu() (which is a React hook) the same way one might use a traditional setState call in a class component. However, they behave quite differently.
In a class component, setState performs a "shallow merge" of the old state and the new state. So if you did:
setState({
texterror: {
status: true,
text: 'it be broke yo'
}
});
If would find only the field texterror on the state object and update that.
However, React hooks work differently. useState() creates a single atomic value, and when you call setValu() it complete replaces the previous value stored in val.
So:
const [val, setValu] = useState({
email: 'example#example.com',
password: 'password',
texterror: {
status: false,
text: ''
},
});
// val is now an object with 'email', 'password', and 'texterror' fields
setValu({
texterror: {
status: true,
text: 'it be broke yo'
}
});
// val is now an object with only a 'texterror' field
In your code when you are using setValu you are wholly replacing the value of val to something that doesn't have an email or password field, which when sent to the server causes an internal server error. A simple fix would be to simply merge the old value of val with the desired new object when you update it:
setValu({
...val,
texterror: {
status: true,
text: 'it be broke yo'
}
});

Remember that with the useState hook you are overwriting the entire state val. This means that you have no longer got a email and password. It is a common oversight when moving from class based components to hooks. Add the parts of the state that didn't change too, and it should all work again.

Related

AxiosError when integrating Stripe with Next.js

I am relatively new to Next.js, and I though I have been encountering some bugs and issues here and there, I have been able to overcome most of them. The latest one I have not been able to figure out, so let's see if somebody else knows what's going on.
I am creating an e-commerce platform on Next.js, Redux and Axios. For the moment I am using fake data to populate the products. When creating a checkout session, the data of the items in the cart is pushed (I can console.log() and I see the items in the terminal. However, the mapping of the checkout session to Stripe is not working. The error I get is an AxiosError: Request failed with status code 500
Error message screenshot
I am trying to add the item data dynamically to the checkout session as follows:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
const { items, email } = req.body;
const transformedItems = items.map((item) => ({
description: item.description,
// if quantities are bundled, this needs to change.
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
images: [item.image],
},
},
}));
const session = await stripe.checkout.sessions.create({
line_items: transformedItems,
mode: 'payment',
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/checkout`,
metadata: {
email,
images: JSON.stringify(items.map((item) => item.image)),
},
});
res.status(200).json({ id: session.id });
};
I have also tried copying the exact code from the Stripe documentation and implementing the changes, but this hasn't changed anything either.
I know, Stripe has made some changes to their API, and that for instance you can't specify anymore with statements like
payment_method_types: ["card"],
anymore. So I took it out.
I have not included any code from the checkout piece, as this seems to be working (as stated, it console.logs() just fine. I can provide this as well though, if someone thinks the issue might be there.
Thanks in advance.
Nela.
Thanks to Code-Apprentice and maiorano84 whose hints in the comments:
A status code 500 means there is an error on the backend. If the server is under your control, then you need to look at the server logs to see what the problem is. The server logs will have a stack trace that shows you where the problem occurs. If you need help understanding the stacktrace, you will need to include it in your question. – Code-Apprentice 22 hours ago
Is this a server-side or client-side AJAX request? If it's the latter, check your network tab to see the full output of your failed request (marked in red in Chrome Devtools). You should be able to get more information about the failed request there. If it's failing on the Stripe side, the Response Headers and Body should have more information there to help you debug. If it's failing on your own success and checkout callbacks, your server logs might have additional information that can help you. – maiorano84 22 hours ago
led me to the answer. I checked my console, and the error that was given was from Stripe. It read as follows:
StripeInvalidRequestError: You cannot use line_items.amount, line_items.currency, line_items.name, line_items.description, or line_items.images in this API version. Please use line_items.price or line_items.price_data.
So I moved the item.description I had outside of the product_data object, into it, and it worked.
The code looks now like this:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
export default async (req, res) => {
const { items, email } = req.body;
const transformedItems = items.map((item) => ({
// if quantities are bundled, this needs to change.
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
description: item.description,
images: [item.image],
},
},
}));
const session = await stripe.checkout.sessions.create({
line_items: transformedItems,
mode: 'payment',
success_url: `${process.env.HOST}/success`,
cancel_url: `${process.env.HOST}/checkout`,
metadata: {
email,
images: JSON.stringify(items.map((item) => item.image)),
},
});
res.status(200).json({ id: session.id });
};

Updating model with GraphQL Mutation

I have been having trouble figuring out how to update a User with graphQL. The functionality I'm currently aiming for is for the user to be able to update their account/profile information. I have some things set up for the user like a bio field for their profile, and a profile picture field that's set up to take a URL and display that as their profile picture.
I have no problems when it comes to creating using graphQL. A user can sign up, log in, make posts, etc without issue. I can also update the User in regards to other models, for example, a new post pushes to the users Post data just fine.
I have not been able to figure out how to update a user directly though. Essentially I can get around this by creating a new model for "profile pic" and pushing that to the User, but that seems like it's just extra steps that might slow things down, as well as shortchanging myself being able to learn something new.
This is the User model. I have omitted a few fields due to the exact block of code being large, but this includes the "image" and "bio" fields (the fields I would like to update) as well as the reference to the Post model which I mentioned above that functions appropriately.
User.js
const userSchema = new Schema(
{
username: {
type: String,
required: true,
unique: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
match: [/.+#.+\..+/, 'Must match an email address!']
},
password: {
type: String,
required: true,
minlength: 8
},
image: {
type: String
},
bio: {
type: String,
maxLength: 500
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post'
}
],
},
Below is the mutation in Explorer, including the variables and the result.
Profile Pic Resolver
addProfilePic: async (parent, { image }, context) => {
if (context.user) {
const updatedUser = await User.findOneAndUpdate(
{ _id: context.user._id },
{ image: image },
{ new: true, runValidators: true }
);
return updatedUser;
}
throw new AuthenticationError('You need to be logged in!');
},
typeDefs.js (relevant only)
type Mutation {
addProfilePic(_id: ID!, image: String!): Auth
}
I notice that in the Explorer page it returns "null" for user with a 200 status. I am led to believe that means that it's not able to even access the "image" field on the user to be able to update it. When compared to my other mutations in regards to users, this is set up very similarly and I'm not sure what the difference is.
I feel like I am missing something very basic here in regards to being able to update. I haven't been able to find an update mutation example that works. Could anyone assist? My main questions would be:
Why does the mutation return "null" for user?
How can I set up my resolver to appropriately update information on an already-created object?
Thank you to anyone who is able to take a look and assist, I will be closely watching this post for replies and will update any other code someone may need to be able to assist. I've been stuck in regards to updating information for a long time, but my site is getting to the point where it's nearly ready and I need to tackle this updating issue in order to progress. Thank you!
Quick Edit: I want to add that "Auth" is referenced. The appropriate authorization headers are in place to retrieve the data. Just wanted to add that in as I highly doubt authorization has anything to do with this!
I have solved this issue and would like to leave the answer here for anyone who may find it useful.
In the mutation typeDefs, I changed the "Auth" to "User",
type Mutation {
addProfilePic(_id: ID!, image: String!): User
}
and then in the mutation itself, took away the user field like such:
mutation addProfilePic($_id: ID!, $image: String!) {
addProfilePic(_id: $_id, image: $image) {
_id
username
image
}
}
This has allowed the user to update their profile photo information. Hope this helps!

React-Query update cache after mutation performed

I have become slightly lost with react-query. Essentially, I have a useQuery to fetch a user from my database. Their details are added to a form and they can update and submit.
The problem I have is that the update is done to a different database. The main database will be batch updated at a later point. As such, instead of refetching the initial data, I need to use setQueryData to update the cache version.
queryClient = useQueryClient()
const { mutate } = useMutation(postUser, {
onSuccess: async (response) => {
console.log(response)
queryClient.cancelQueries('user');
const previousUser = queryClient.getQueryData('user');
console.log(previousUser)
queryClient.setQueryData('user', {
...previousUser,
data: [
previousUser.data,
{ '#status': 'true' },
],
})
return () => queryClient.setQueryData('user', previousUser)
}
})
At the moment I have something like the above. So it calls postUser and gets a response. The response looks something like so
data:
data:
user_uid: "12345"
status: "true"
message: "User added."
status: 1
I then getQueryData in order to get the cache version of the data, which currently looks like this
data:
#userUuid: "12345"
#status: ""
message: "User found."
status: 1
So I need to update the cached version #status to now be true. With what I have above, it seems to add a new line into the cache
data: Array(2)
0: {#userUuid: "12345", #status: ""}
1: {#status: "true"}
message: "User found."
status: 1
So how do I overwrite the existing one without adding a new row?
Thanks
This is not really react-query specific. In your setQueryData code, you set data to an array with two entries:
data: [
previousUser.data,
{ '#status': 'true' },
],
first entry = previousUser.data
second entry = { '#status': 'true' }
overriding would be something like this:
queryClient.setQueryData('user', {
...previousUser,
data: {
...previousUser.data,
'#status': 'true',
},
})
On another note, it seems like your mixing optimistic the onMutate callback with the onSuccess callback. If you want to do optimistic updates, you'd implement the onMutate function in a similar way like you've done it above:
cancel outgoing queries
set data
return "rollback" function that can be called onError
This is basically the workflow found here in the docs.
if you implement onSuccess, you're updating the cache after the mutation was successful, which is also a legit, albeit different, use-case. Here, you don't need to return anything, it would be more similar to the updates from mutation responses example.

react-native-gifted-chat showing same message multiple time

I'm using react-native-gifted-chat in my react-native app. As I shown in this image, there is same message displayed multiple time and message: Yes getting new msg 's place is also varied from it's actual position.
My issue is same as this. Can anyone please help me to solve this.
Thank you in advance.
I got a solution of my question. #Ron you are right but in my case the issue is different. I solved it by change my format of parameters. It took different format and I passed different so they conflicted each other. Here is the solution it may useful to others.
parse = snapshot => {
const { timestamp: numberStamp, text } = snapshot.val();
const { key: _id } = snapshot;
const createdAt = moment(snapshot.val().createdAt, "DD/MM/YYYY hh:mm:ss");
const user = { };
var temp_data = snapshot.val()
if(snapshot.val().name == this.state.temp_logged_name) {
user._id = 1;
user.name = temp_data.name;
user.avatar = temp_data.avatar;
}
const message = {
_id,
createdAt,
text,
user,
};
return message;
};
I had encountered this issue as well. I had set up react-native-gifted-chat on my mobile app. And at the other end I had set up a simple HTML page with a script to initialise the Websocket connection and send messages on the onsend event. What I had realised later that while the unique id was getting generated at the app end (because the id was being generated by the library itself), nothing of such sort existed at the other end.
Basically, this weird behaviour crops up when a unique id _id is missing for a message. Each message must have at least the following properties while executing GiftedChat.append(previousMessages, messages).
{
_id: 1,
text: 'Hello developer',
createdAt: new Date(),
user: {
_id: 2
}
}
There could be two reasons behind it,
1) Each message should be passed a unique id, so just use uuidv4 npm package and append it to _id prop of the object.
Example:
messages: GiftedChat.append(previousState.messages, {
_id: uuidv4(), // or use Math.round(Math.random() * 1000000)
text: text,
createdAt: new Date(),
user: {
_id: 2,
name: "React Native",
avatar: "https://placeimg.com/140/140/any"
},
image: attachment
})
2) Second possibility could be on the gateway you are using to initiate the chat between users. So, some gateways have known issues to repeat the message multiple times. You could to string comparison each time a new message is received and pushed to the chat screen, however it is not advised to do this.
I figured this out by simply applying the filter to the incoming message in useLayout Effect:
useLayoutEffect(() => {
db.collection('Chats').doc(docID).collection('messages').orderBy("createdAt", "desc").onSnapshot(snapshot => {
setMessages(
prev =>
prev
.filter((ftr,index,self) => ftr?.user?._id !== loginUser?.id) //login user id is the current user's id you can do the same for recieved messages
.concat
(snapshot.docs.map(doc => doc.data({
_id: doc?.id,
user: doc.data().user,
text: doc.data().text,
createdAt:new Date(doc.data().createdAt),
})
))
)
})
},[])
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Mutation with OptimisticResponse doesn't include server response data in second Update

Similar to the question here I have found that when using optimisticResponse and update for a mutation, that the id set from the response of the server is wrong. Furthermore, the id actually gets set by running the optimistic function again.
In the mutation below the refetchQueries is comment out on purpose. I don't want to use that. I want to manage everything through the update only.
Also notice the optimisticResponse id has a "-" prepended to it to prove the optimistic function is run twice:
id: "-" _ uuid(),
Mutation
graphql(MutationCreateChild, {
options: {
// refetchQueries: [{QueryAllChildren, variables: {limit: 1000}}],
update: (proxy, {data: {createChild}}) => {
const query = QueryAllChildren;
const data = proxy.readQuery({query});
data.listChildren.items.push(createChild);
proxy.writeQuery({query, data});
console.log("id: ", createChild.id);
}
},
props: props => ({
createChild: child => {
return props.mutate({
variables: child,
optimisticResponse: () => ({
createChild: {
...child,
id: "-" + uuid(),
__typename: "Child"
}
})
});
}
})
})
The output from the console.log statement is:
id: -6c5c2a28-8bc1-49fe-92e1-2abade0d06ca
id: -9e0a1c9f-d9ca-4e72-88c2-064f7cc8684e
While the actual request in the chrome developer console looks like this:
{"data":{"createChild":{"id":"f5bd1c27-2a21-40c6-9da2-9ddc5f05fd40",__typename":"Child"}}}
Is this a bug or am I not accessing the id correctly in the update function?
It's a known issue, which has now been fixed. I imagine it'll get released to the npm registry soon.
https://github.com/awslabs/aws-mobile-appsync-sdk-js/pull/43
https://github.com/awslabs/aws-mobile-appsync-sdk-js/commit/d26ea1ca1a8253df11dea8f11c1749e7bad8ef05
Using your setup, I believe it is normal for the update function to be called twice and you are correct that the real id from the server will only be there the second time. Apollo takes the object you return from optimisticResponse and passes it to the update function so your UI can immediately show changes without waiting for the server. When the server response comes back, the update function is called again with the same state (i.e. the state without the optimistic result) where you can reapply the change with the correct value from the server.
As for why the second id you list with the '-' is not the same as the id you see in the chrome dev console, I am not sure. Are you sure that it was actually that request that matched up with that call to console.log?

Resources