State not updating within a promised fetch (react) - javascript

I have a form with which i am sending data to my api. The data is used to create backend admin accounts and also create post entries. I need to post the data in a certain order to get some dynamic fields from the admin account to the post. For example:
create the admin account to make an id field
use the id field and apply to the post to create a unique link between admin account and post
Everything works apart from one state update that i'm trying to do after fetching the freshly created account:
// dont know what the ID will be as its dynamic
const [theID, setTheID] = useState('');
//Send the data to firstly create the admin account (works fine)
fetch('example.com/api/cockpit/saveUser', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Cockpit-Token': process.env.REACT_APP_API_KEY
},
body: JSON.stringify({
user: {
user: firstname.toLowerCase()+surname.toLowerCase(),
name: firstname,
email: email,
password: password,
group: 'consumers',
}
})
})
.then(user => user.json())
// make next fetch a chain of the promise
// fetch all accounts but filter by email so will only return the right one
.then(()=>{
return fetch(`example.com/api/cockpit/listUsers&filter[email]=${email}`,{
method: 'post',
headers: {
'Content-Type': 'application/json',
'Cockpit-Token': process.env.REACT_APP_API_KEY
}
})
})
.then((res) => {
return res.json();
})
.then((res)=>{
// the console log works fine
console.log(res[0]._id);
// the state returns undefined
setTheID(res[0]._id);
})
.catch((err)=>{
console.error(err);
})
//Then last fetch to make the post entry works fine ...
.then(()=>{
return fetch('example.com/api/collections/save/consumers', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Cockpit-Token': process.env.REACT_APP_API_KEY,
},
body: JSON.stringify({
data: {
firstname: firstname,
surname: surname,
password: password,
email: email,
project_name: projectName,
project_template: tmp,
// expected to get the dynamic ID front the state
ass_consumer: theID,
slug: projectName.toLowerCase().replace(' ','') + '-rr',
username: firstname.toLowerCase()+surname.toLowerCase()
}
})
})
})
.then(user => user.json())
.then((user)=>{
setnewPage(user._id);
})
I've checked the console which shows up fine after fetching. All my other state changes are bound to the form inputs but i've never had this problem with updating state in a fetch before.
I've also tried creating a function that takes the email as an argument then returns the state change but no luck with that either.
Appreciate any help!
(using cockpit as my api)
EDIT
This is what the console log returns:
(i just need the _id)
{
user: "johndoe",
name: "john",
email: "john.doe#example.com",
active: true,
group: "consumers", 
_created: 1627039008,
_id: "60faa5203365618a38000035",
_modified: 1627039008,
}

You are most likely reading the state within the same operation ("too quickly"), so the state changes will not be reflected yet because the process to update React state is asynchronous.
You have 2 options to always get the most up to date state value:
Either use a reference to the state (check out this package)
Or use an "ugly" work-around taking the value from setState like this:
const [id, setId] = useState("")
setId("test")
console.log(id) // ""
// do this to get the up to date value, as the value
// in setState is always the most up to date one
let idVal
setId(current => {
idVal = current
return current
})
console.log(idVal) // "test"
So in your specific example, try this:
//Then last fetch to make the post entry works fine ...
.then(()=>{
let customerID
setTheID(current => {
customerID = current
return current
})
return fetch('example.com/api/collections/save/consumers', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Cockpit-Token': process.env.REACT_APP_API_KEY,
},
body: JSON.stringify({
data: {
[...]
// use the customerID variable, not the state variable
ass_consumer: customerID,
[...]
}
})
})
})

Related

React Router Adds URL Query Params on POST Request

Hello my fellow nerds,
I am running into an issue where when I make a POST request (using Axios library inside of React function), it automatically appends all of the data from the "Create a User" form into search parameters in the URL of the page upon submission. I don't like this because I'd rather this stay hidden within the POST request, not flung out onto the page URL.
Image: URL after user was created
This is a direct result of React Router adding the data sent in the request of the body being appended to the location.search of react router's history object. So, naturally, since the react router history object is mutable, I tried adding this to the response of the submission:
this.props.location.search.replace({*some random parameter stuff here*});
This was in hopes it would remove all of that stuff from the URL and redirect to the page without search parameters. Anyways, I have seen a few other posts similar in nature but they don't seem to answer this exact question.
TL;DR: I am trying to send a POST request without React Router adding my req.body data to the params in my URL and the location.search object.
MY CODE:
Users.js: (front end)
handleSubmit (e) {
Axios.post(`${server}/s/admin/users/create`, {
headers: {
'Content-Type': 'application/json'
},
data: {
firstName: this.state.firstName,
lastName: this.state.lastName,
username: this.state.username,
password: this.state.password,
email: this.state.email
}
}).then((res) => {
console.log(res);
}).catch(err => console.log(err));
}
users.js: (back end)
app.post("/s/admin/users/create", (req, res) => {
let rb = req.body.data;
let newUser = new User({
_id: uid.time(),
orgID: req.session.orgID,
email: rb.email,
username: rb.username,
password: bcrypt.hashSync(rb.password, hashRate),
firstName: rb.firstName,
lastName: rb.lastName,
data: { exist: "true" },
settings: { exist: "true" }
});
// Save new owner to db
newUser.save((err, data) => {
if (err) return console.error(err);
res.json({info: `A new user, ${newUser.firstName} ${newUser.lastName}, has been created successfully.`});
});
});
Thank you!
P.S. This is my first post, thank you for your patience. I've tried searching and solving this issue for about a day now.
Can you try rewriting it this way? cause axios.post expect
axios({
method: 'post',
url: `${server}/s/admin/users/create`,
headers: {
'Content-Type': 'application/json'
},
data: {
firstName: this.state.firstName,
lastName: this.state.lastName,
username: this.state.username,
password: this.state.password,
email: this.state.email
}
}).then((res) => {
console.log(res);
}).catch(err => console.log(err));
Axios.post method expect the second argument as data but you have passed config, another way is to pass data and config in the third argument.
handleSubmit(e) {
Axios.post(`${server}/s/admin/users/create`, {
firstName: this.state.firstName,
lastName: this.state.lastName,
username: this.state.username,
password: this.state.password,
email: this.state.email
}, {
headers: {
'Content-Type': 'application/json'
}
}
}).then((res) => {
console.log(res);
}).catch(err => console.log(err));
}

problems with stripe and react state/global state

I am trying to set up stripe.js with my app. I have literally every single thing done and when the purchase is complete stripe passes the client to a success page. The problem I am having is that stripe seems to be resetting my application (the props + state from previously rendered components before stripe.js' checkout are not being passed through) in order to properly show the appropriate data on this success page i need the state from previous components.
I am using stripe's redirectToCheckout function and it redirects out of the app .maybe a serverless function from JAMstack could send the data between stripe? Someone please let me know! Here is some of my code below for the stripe part of my app. There isn't really a specific part of my code to show that would help answering this question tbh. It's a fundamental question of whether there is a way to not have stripe reset my app thus losing previous state after redirecttoCheckout function.
The response (or data on products and session etc) is coming from a serverless netlify function. which is why i mentioned it above. Maybe that could be a possible help
//client sides
import {loadStripe} from "#stripe/stripe-js"
export async function handleFormSubmission(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
sku: form.get('sku'),
quantity: Number(form.get('quantity')),
};
const response = await fetch('/.netlify/functions/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => res.json())
const stripe=await loadStripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
if(err){
console.log(err)
}
}
//client sides
import {loadStripe} from "#stripe/stripe-js"
export async function handleFormSubmission(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
sku: form.get('sku'),
quantity: Number(form.get('quantity')),
};
const response = await fetch('/.netlify/functions/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => res.json())
const stripe=await loadStripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
if(err){
console.log(err)
}
}

How to extract localstorage values in reactjs

I am using form in reactjs. I stored user data in localstorage now I want to upload file and i want to store file key with userID which is already stored in localstorage. With localstorage.getItem('user') I am able to show user values but did not use in key, when i display user values in console it give me message with undefined. Could you please help me how i can set key with userid.
Code
handleUpload = e => {
const reader = new FileReader();
const storeUser = JSON.parse(localStorage.getItem('user'));
reader.onload = function(upload) {
fetch(`http://...../s3/uploadtoaws`, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({
userId: storeUser._id,
type: 'employee',
content: upload.target.result,
key: this.userId,
oldKey: '',
}),
})
.then(response => response.json())
.then(res => {
console.warn(res);
})
.done();
};
reader.readAsDataURL(e.file.originFileObj);
};
Use window object to store your data if it is in-incentive
Like,
// Store like this..
window.user = {"_id": 1, "userName": "Bhavin00"};
// Use like this..
console.log(window.user);
or you can use npm package like :- reactjs-localstorage
let user = JSON.parse(localStorage.getItem('user'));
const { id: userId } = user;
Now you can use userId in the above request you shared.

How to post query parameters with Axios?

I am trying to post on an API with some query params.
This is working on PostMan / Insomnia when I am trying to by passing mail and firstname as query parameters :
http://localhost:8000/api/mails/users/sendVerificationMail?mail=lol%40lol.com&firstname=myFirstName
However, when I am trying to do it with my react native app, I got a 400 error (Invalid Query Parameters).
This is the post method :
.post(`/mails/users/sendVerificationMail`, {
mail,
firstname
})
.then(response => response.status)
.catch(err => console.warn(err));
(my mail and firstname are console.logged as follow: lol#lol.com and myFirstName).
So I don't know how to pass Query Parameters with Axios in my request (because right now, it's passing data: { mail: "lol#lol.com", firstname: "myFirstName" }.
axios signature for post is axios.post(url[, data[, config]]). So you want to send params object within the third argument:
.post(`/mails/users/sendVerificationMail`, null, { params: {
mail,
firstname
}})
.then(response => response.status)
.catch(err => console.warn(err));
This will POST an empty body with the two query params:
POST
http://localhost:8000/api/mails/users/sendVerificationMail?mail=lol%40lol.com&firstname=myFirstName
As of 2021 insted of null i had to add {} in order to make it work!
axios.post(
url,
{},
{
params: {
key,
checksum
}
}
)
.then(response => {
return success(response);
})
.catch(error => {
return fail(error);
});
In my case, the API responded with a CORS error. I instead formatted the query parameters into query string. It successfully posted data and also avoided the CORS issue.
var data = {};
const params = new URLSearchParams({
contact: this.ContactPerson,
phoneNumber: this.PhoneNumber,
email: this.Email
}).toString();
const url =
"https://test.com/api/UpdateProfile?" +
params;
axios
.post(url, data, {
headers: {
aaid: this.ID,
token: this.Token
}
})
.then(res => {
this.Info = JSON.parse(res.data);
})
.catch(err => {
console.log(err);
});
You can use params and body together in a request with axios
sendAllData (data) {
return axios
.post(API_URL + "receiveData", JSON.stringify(data), {
headers: { "Content-Type": "application/json; charset=UTF-8" },
params: { mail: xyx#example.col }, //Add mail as a param
})
.then((response) => console.log("repsonse", response.status));
}

How to store asynchronous data inside a variable Fetch API

I am working on a project that uses Laravel for back-end and React for front-end.
Whenever the user clicks login, a modal will open that will, up on submitting, fetch the users bearer token.
These fetch functions are all located inside AuthService.js
export function getLoginToken(email, password) {
fetch("/api/login", {
method: "post",
credentials: 'include',
headers: {
'Accept': 'application/json',
"Content-type": "application/json",
},
body: JSON.stringify({
'email': email,
'password': password
})
}).then((response) => response.json())
.then((responseJSON) => {
return responseJSON;
})
}
The Nav component is where the login button resides, so I'm importing and using the funcionts that I've created inside AuthService.js
When I console log the json result within the actual getLoginToken function, there is no problem. Because up on placing it inside .then() it waits for it to complete so it doesn't result in undefined.
Now.. Inside the Nav component, there is an onClick function bound to the login button, which will execute AuthService.getLoginToken(email, password)
Actual problem:
I would like to store the response data inside a variable, but i keep getting undefined. This because I'm trying to insert asynchronous data inside a synchronous function.
I've also tried:
AuthService.getLoginToken(loginEmail, loginPassword).then((result) => {
this.setState({
token: result
});
});
But this will also return: Cannot read property 'then' of undefined.
Any ideas on how to fix this?
Try this
export function getLoginToken(email, password) {
return fetch("/api/login", {
method: "post",
credentials: 'include',
headers: {
'Accept': 'application/json',
"Content-type": "application/json",
},
body: JSON.stringify({
'email': email,
'password': password
})
}).then((response) => response.json())
.then((responseJSON) => {
return responseJSON;
})
}
Proper way would be to use state management tool like redux
You can use async await. Or use redux-thunk if you are doing redux.

Categories