How to update data in ReactJS using JSON server - javascript

I am new to react and I can fetch the data from JSON file . Now I need to update those values and submit to the JSON file . I am struggling to submit updated input field in JSON file
submitEmpData(event) {
event.preventDefault();
this.state.empProjects.allocation=this.state.allocation;
this.setState({
empProjects:this.state.empProjects
});
return fetch('http://localhost:3000/Employee/' + this.state.empProjects.id, {
method: 'PUT',
mode: 'CORS',
body: this.state.empProjects,
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
return res;
}).catch(err => err);
}

There are some ways to trigger a render in react:
You send new values down as props to a child component, or
You use a form of state (hooks or setState for example) to update a components state.
In your example, add a setState once the fetch promise has either rejected or resolved, setting the data needed for rendering.

I have restructured the code for better understanding. I believe JSON.stringify() and res.json() may places where you might need to look into.
async submitEmpData(event) {
event.preventDefault();
let { empProjects, allocation } = this.state;
empProjects.allocation = allocation;
// updating the state
this.setState({
empProjects,
});
// calling the api
try {
let res = await fetch("http://localhost:3000/Employee/" + this.state.empProjects.id, {
method: "PUT",
mode: "CORS",
body: JSON.stringify(this.state.empProjects),
headers: {
"Content-Type": "application/json"
}
})
return await res.json();
} catch (err) {
return err;
}
}
Kindly ping me in comments for any clarification

The following method is wrong to change the JSON because the this.state is an object and javascript will check the reference for comparison (See this).
this.state.empProjects.allocation=this.state.allocation;
this.setState({
empProjects:this.state.empProjects
});
instead of this, you can use the spread operator to create a new object:
this.setState({
...this.state, this.state.empProjects: { ...this.state.empProjects, allocation: "NEW value"}
})
Also to send the request using fetch, the body must match with content type:
body: JSON.stringify(this.state.empProjects)

Related

How can I update the message body using gmail API in javascript

I have got the message body. Now I want to update it according to my needs. I am using this login/code. but it says 400 error. I think issue is in body parameter of the request. Would you please help me there?
var token = localStorage.getItem("accessToken");
var messageId = "18514426e2b99017";
async function updateMessageBody() {
var updatedBody = "Hello,\n\nThis is the UPDATED message body.\n\nBest regards,\nJohn";
const API_KEY = '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-CXIk';
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
const response = await fetch(`https://gmail.googleapis.com/gmail/v1/users/me/messages/18514426e2b99017/modify?key=['API_KEY']`, {
method: 'POST',
headers: headers,
body: JSON.stringify({
raw: window.btoa(unescape(encodeURIComponent(updatedBody)))
})
});
if (!response.ok) {
// throw new Error(`Request failed with status code ${response.status}`);
}
return await response.json();
}
updateMessageBody()
.then(response => {
console.log('Message body updated successfully:', response);
})
.catch(error => {
});
Checking the documentation, it states that a message body can't be altered once it has been created, meaning that once you have already created an email this message can't be changed. You can verify this here.
You can instead update a message draft which is possibly what you are trying to do, however using the endpoint you have in your code this won't be possible and will lead to the error message you are getting, try using instead the users.draft.update method that allows you to modify the content of the draft sitting in your mailbox. Please note as well that using the method users.messages does not have any update method as they only have the modify one's, those methods can only update the labels though so please be aware of that.

How to make addition of state in react native

I want to addition of two state,
the both value of state are extracted from json object using a fetch request
constructor(props) {
super(props);
this.state = {
charge:0,
total:0,
};
}
dataFatch()
{
const tot = this.state.total;
fetch('https://xyz.in/app/reg.php', {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'item_id':ids,
'func':'item_data',
})
}).then((response) => response.json())
.then((json) => {
this.setState({'total':json.total});
this.setState({'charge':json.charge});
}).catch((err) => { console.log(err); });
}
but after a fetching data charge=30 and total=80
i preform addition like
Let add = this.state.charge+this.state.total;
console.log(add);
but the result is 3080 instead of 110
so how can i solve this problem
It looks like an you are doing concat strings instead of number
I don't see add logic in your api request function but as per you showed you can fix it with below
let add = Number(this.state.charge) + Number(this.state.total);
console.log(add);
But important is you understand that the state you stored isn't number but typeof string.
So while updating state you can make sure it's number not an string so you don't need to convert it into number while doing concat.

Typescript removes Authorization header from POST and PATCH fetch requests

I've built an API using C# that uses JWT tokens for authorization. On the frontend I store these tokens in local storage and get them, when creating a request. When creating GET or DELETE requests, everything works fine, and using console.log() I can see that fetch options have the Authorization header added. However when using POST or PATCH methods, the Authorization header is missing immediatly after adding it to the object. Here is my request method:
const send = async (apiOptions: ApiParams): Promise<FetchReturn> => {
const accessToken = GetAccessToken()
const options: ApiOptions = {
method: apiOptions.method,
headers: {
Authorization: `Bearer ${accessToken}`
}
}
console.log(options)
if (apiOptions.data) {
options.headers = {
'Content-Type': 'application/json'
}
options.body = JSON.stringify(apiOptions.data)
}
const result = await fetch(`${getUrl()}/${apiOptions.path}`, options).then(res => res).catch(err => err)
if (!result.ok) {
if (IsExpired()) {
const refreshResult = await fetch(`${getUrl()}/api/user/refresh`, {method: 'POST', headers:{
'Content-Type': 'application/json'
}, body: JSON.stringify(GetRefreshRequest())}).then(res => res).catch(err => err)
if (refreshResult.ok) {
Login(JSON.parse(await refreshResult.text()))
return await send(apiOptions)
} else if (refreshResult.status === 401) {
Logout()
window.location.reload()
return { code: 0, text: ""}
}
}
}
const text = await result.text()
return { code: result.status, text: text }
}
I suppose that in apiParams for POST you have property 'data' assigned, and later you have if-condition that completely replaces request headers object.
Change it to:
options.headers['Content-Type'] = 'application/json';
To keep authorization in headers
The first time check your apiOptions.data
i think , its null when you call POST/Patch request
Just put console.log("...") In the if statement , Then try for resolve your Error
If your problem not resolved, put a replay under my post

Vuex: Fetch data with axios based on already fetched data

I'm fetching data from an external API via axios in the Vuex store, which gives me an array with objects - let's say - cities.
Each city has a zipCode, which I need to fetch city details with.
I want to add that details-object as a new key to the cities array.
Right now I'm fetching all cities and have another Vuex action to fetch the city details.
ACTION to fetch all cities
fetchCities: ({ commit, state, dispatch }) => {
let baseUrl = "https://my-url/cities";
let config = {
headers: {
accept: "application/json",
Authorization: ...
},
params: {
param1: "a",
param2: "b"
}
};
axios
.get(baseUrl, config)
.then(function(response) {
commit("UPDATE_CITIES_RAW", response.data.items);
})
.catch(function(error) {
console.log(error);
});
dispatch("fetchCityDetails");
},
MUTATION after fetching all cities
UPDATE_CITIES_RAW: (state, payload) => {
state.citiesRaw = payload;
},
Each object in that array has a zipCode, which I'm fetching details about this city with.
I tried to loop the citiesRaw array inside the action to fetch the details and commit a change for each iteration, but the array from the state is empty at this point, because the action gets called before the mutation.
ACTION to fetch city details
fetchCityDetails: ({ commit, state }) => {
let baseUrl = "https://my-url/cities/"
let config = {
headers: {
accept: "application/json",
Authorization: ...
}
};
// citiesRaw is empty at this point
state.citiesRaw.forEach(e => {
let url = baseUrl + e.zipCode;
axios
.get(url, config)
.then(function(response) {
commit("UPDATE_CITY_DETAILS", {
response: response.data,
zipCode: e.zipCode
});
})
.catch(function(error) {
console.log(error);
});
});
},
What are the best ways to wait for the first fetch and then update the array?
Should I even use the same array or create a new one to begin with?
Or is there even a better way to fetch based on fetched data in the Vuex store?
UPDATE
After fixing the dispatching before the async function even finished (thanks, #Y-Gherbi), I also refactored the way of fetching the details:
Component dispatches  fetchCities
in fetchCities action: commit  UPDATE_CITIES
in UPDATE_CITIES mutation: .map on the payload and create new object -> push all to state.cities
in fetchCities action: loop state.cities & dispatch  fetchCityDetails(zipCode) for each city
in fetchCityDetails action: commit  UPDATE_CITY_DETAILS
in UPDATE_CITY_DETAILS mutation: .map on state.cities  and add  cityDetails object  to the referred city object
new actions
fetchCities: ({ commit, state, dispatch }) => {
let baseUrl = "https://my-url/cities";
let config = {
headers: {
accept: "application/json",
Authorization: ...
},
params: {
param1: "a",
param2: "b"
}
};
let url = baseUrl;
axios
.get(url, config)
.then(function(response) {
commit("UPDATE_CITIES", response.data.items);
state.cities.forEach(city => {
dispatch("fetchCityDetails", city.zipCode);
});
}
})
.catch(function(error) {
console.log(error);
});
},
fetchCityDetails: ({ commit }, zipCode) => {
let baseUrl = "https://my-url/cities";
let config = {
headers: {
accept: "application/json",
Authorization: ...
},
};
let url = baseUrl + "/" + zipCode;
axios
.get(url, config)
.then(function(response) {
commit("UPDATE_CITY_DETAILS", {
cityDetails: response.data,
zipCode: zipCode
});
})
.catch(function(error) {
console.log(error);
});
}
new mutations
UPDATE_CITIES: (state, cities) => {
// I don't need all data & I want to rename the keys from the response,
// so I create a new object
cities = cities.map(city => {
let obj = {};
obj.zipCode = city.zip_code
obj.key1 = city.key_1;
obj.key2 = city.key_2;
return obj;
});
state.cities.push(...cities);
},
UPDATE_CITY_DETAILS: (state, payload) => {
let cities = state.cities;
// add one details-object for each city
cities = cities.map(city => {
if (city.zipCode == payload.zipCode) {
city.cityDetails = payload.cityDetails;
}
return city;
});
state.cities = cities;
}
The question remains: Is there a better/more optimized approach to this kind of fetching?
I think that there are much better and elegant ways to deal with this but to simply help you with the bug (looping over an empty array that's expected to be an array of cities)
fix
Try moving the dispatch function to the next line after the commit function. The array (citiesRaw) is empty because you are calling the action before the data is fetched (because axios.get is an async operation)
alternative solution
As you said, the cities are displayed in a table with expandable rows, which are used to display the city details. Fetching 100-1000 cities is quite a lot of data but it's still one call to the back-end. But looping over all these items and doing a request for every one of them could be problematic from a performance and bandwidth (if that's the correct term for large data usage) perspective.
Personally, I would handle each request whenever the user actually needs this data. In your case whenever the user clicked on a row to expand it.
Storing it in the store?
Whenever you want to store the city details in the store is up to you. You could just keep it in the component but personally, I would use it as some kind of cache mechanism.
When the user clicked on a row, check if the details are already fetched
if(!state.cityDetails[key]) {
axios.get()
.then(res => {
// Commit a mutation that saves the fetched details
})
.catch((error) => {
// Handle error
})
} else {
// Use the 'cached' cityDetails
}
Your store could look something like this:
{
cityDetails: {
keyCouldBeIdOrZipcode: {...},
anOtherAlreadyFetchedCity: {...},
}
}
sorry for the typos and bad formatting. Typing code on a phone is horrible

React Native AsyncStorage getItem returns promise not value

I have a login form and I am able to post the form values. After the successful POST request, I get authentication token returned from the API. I need to save this token for future reference in local storage. For saving this auth token I am using AsyncStorage. I used AsyncStorage.setItem(STORAGE_KEY, responseData.auth_token); setItem method to save the data.
If I console log this by :
console.log(AsyncStorage.setItem(STORAGE_KEY));
it returns as promise object like this
Promise {_45: 0, _81: 0, _65: null, _54: null}
_45
:
0
_54
:
null
_65
:
"efcc06f00eeec0b529b8"
_81
:
1
__proto__
:
Object
how can I get the exact value from the AsyncStorage.getItem method?
This is my fetch method
fetch('http://54.255.201.241/drivers', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
first_name: this.state.firstName,
mobile_number: this.state.mobileNumber,
vehicle_number: this.state.vehicleNumber,
vehicle_type_id: 1,
})
}).then((response) => response.json()).then((responseData) => {
if (JSON.stringify(responseData.mobile_number) == (this.state.mobileNumber))
{
AsyncStorage.setItem(STORAGE_KEY, responseData.auth_token);
console.log(AsyncStorage.getItem(STORAGE_KEY));
this.props.navigator.push({id: 'Otp'})
}
else
{
Alert.alert("SignUp Failed","Mobile number already taken.")
}
})
.done();
BTW in documentation they have used await. I tried using that but the pages are not loading.Here with attached the screenshot.
using Async/Await :
async _getStorageValue(){
var value = await AsyncStorage.getItem('ITEM_NAME')
return value
}
You have to call as
getUserToken().then(res => {
console.log(res);
});
Since this is an async call.
This is how I usually manage to get the Value from The Local Storage:
const getItemFromStorage = async () => {
try {
await AsyncStorage.getItem('ITEM_NAME', (error: any, result: any) => {
if (result) {
console.log(result);
}else{
console.log(JSON.stringfy(error));
}
});
} catch (error) {
console.log(error);
}
}

Categories

Resources