I'm calling a function that needs to do 2 things:
Call a parent-level function to update my database using a fetch request and then update my parent-level state
Wait for 1st function to update my state, and then route to a view where my updated data is seen (using props)
The problem is I'm not sure how to wait for this 1st function to be complete before moving on to the 2nd. So when I try to change views with this 2nd function, the data shown isn't updated.
The parent-level function that I would like to run and update my state.
updateEntry = (newHeader, newBody, index) => {
fetch("http://localhost:3000/update", {
method: 'PUT',
body: JSON.stringify({
header: newHeader,
body: newBody,
index: index
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => { this.setState({journalDb: data })})
}
The child level function where I call the parent level function using updateEntry()
initiateUpdate = (e) => {
e.preventDefault();
this.props.updateEntry(value1, value2, value3)
this.props.setEditMode();
}
The issue is my parent level state isn't updating in time to show when I change the route using setEditMode()... I'm not sure how to wait for the parent state to update before running this
so what you could do is do something like this
updateEntry = (newHeader, newBody, index) => {
return new Promise((resolve) =>fetch("http://localhost:3000/update", {
method: 'PUT',
body: JSON.stringify({
header: newHeader,
body: newBody,
index: index
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
this.setState({journalDb: data })
resolve()
})
})
and simply add
initiateUpdate = (e) => {
e.preventDefault();
this.props.updateEntry(value1, value2, value3)
.then(() => this.props.setEditMode());
}
this way you're guaranteed that updateEntry was finished when setEditMode is called
Just change your updateEntry function to return the result of the fetch, as this is a promise. For example:
updateEntry = (newHeader, newBody, index) => fetch("http://localhost:3000/update", {
Then when you call the parent function, treat it just like a promise:
this.props.updateEntry(value1, value2, value3).then(() => {
this.props.setEditMode()
})
Related
This week I have learned to fetch data from an API with javascript and jQuery.
Until now, I've only had to fetch from deeper levels within objects (which I've succeeded at), but I still don't know how to post to specific elements within other objects.
I'm currently working on a smart home project, where I'm the one responsible for the web application.
All device controllers have got a 'favourite' button, which is the one that triggers this function to either favourise or un-favourise the pressed object:
function toggle_favourite(id) {
fetch('../../api/objects?id=' + id)
.then(response => response.json())
.then(data => {
if (data.objects[id-1].favourite == true) {
// set .favourite to 'false'
put(id, {
favourite: false
})
} else {
// set .favourite to 'true'
put(id, {
favourite: true
})
}
})
})
}
function put(id, data) {
fetch('../../api/objects?id='+id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
}
The data that I'm trying to change is this favourite value.
How do I manouver over to this 'favourite' value with fech/'PUT'?
If the value isn't top level, you have to fetch the entire object, change the part you want to and then 'PUT'/'POST' the object at the end.
For this example, I fetched the entire object and saved it into a 'const', went through it and changed the 'favourite' value, and at the end I 'PUT' everything back into the object like so:
async function getObject(id){
const response = await fetch('../../api/objects?id='+id)
return response.json()
}
async function saveObject(){
const data = await getObject(int_id)
$.each(data, function(index, objects){
$.each(objects, function(index, values){
if (values.favourite == true ){
values.favourite = false
}
else{
values.favourite = true
}
})
})
put(int_id, data)
function put(id, data) {
fetch('../../api/objects?id='+id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
})
}
}
I'm completely new to React and I'm trying to use the React Hooks. I have a functional based component and I'm using the useState and useEffect to get users from my database and display them in a table.
Now, I also have a delete button for each row of my table. When I click on the delete button, I execute a delete function which deletes the data from my database. This works well. However, the table is not updated unless I refresh the whole page completely.
How can I update (re-render) my users table once the delete is done.
Below is a snippet of my code:
const [users, listUsers] = React.useState([]);
React.useEffect(() => {
axios
.get(GET_URL)
.then(res => {
console.log(res.data);
listUsers(res.data);
})
.catch(err => console.log(err));
}, []);
const deleteUser = async id => {
await fetch(DELETE_URL, {
//JSon message
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: id
})
})
.then(response => response.text())
.then(responseJson => {
console.log(JSON.stringify(responseJson));
})
.catch(error => {
console.error(error);
});
alert('User Deleted.');
};
You are not updating your list of users state once deletion You have update your list of users state. You can do this by:
const deleteUser = async id => {
await fetch(DELETE_URL, {
//JSon message
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: id
})
})
.then(response => response.text())
.then(responseJson => {
console.log(JSON.stringify(responseJson));
})
.catch(error => {
console.error(error);
});
const usersUpdated = users.filter(p => p.id !== id); //Filter your list of users and remove the one for the specific id
listUsers(usersUpdated); //This updates your state
alert('User Deleted.');
};
;)
I'm trying to consolidate some code in one of my react components because my componentDidMount method is getting a bit verbose. This gave me the idea to create an api that does all of my data fetching for the entire app.
I'm having an asynchronous issue I'm not sure how to resolve.
I created the separate api file (blurt.js):
exports.getBlurts = function() {
var blurtData = null;
fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((data) => {
blurtData = data;
});
return blurtData;
}
and imported it to my (.jsx) component via
import blurtapi from '../api/blurt.js';
The problem is that when I call blurtapi.getBlurts() within componentDidMount(), the value comes back as null. However, if I write the data to the console like so:
.then((data) => {
console.log(data);
});
all is as it should be. So, the function is returning before the db operation completes, hence the null value. How would I reign in the asynchronous aspect in this case? I tried an async.series([]) and didn't get anywhere.
Thanks
So fetch returns a promise, which it is async , so any async code will run after sync code. so this is the reason you get null at first.
However by returning the async function , you are returning a promise.
Hence this code:
exports.getBlurts = async () => {
const data = await fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
const jsonData = await data.json();
return jsonData;
}
To retrieve any promise data, you need the then function,
so in your componentDidMount, you will do:
componentDidMoint() {
blurtapi.getBlurts()
.then(data => console.log(data)) // data from promise
}
Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
async/await:
https://javascript.info/async-await
I hope this makes sense.
fetch call returns a promise. therefore in your function u do something like this
exports.getBlurts = function() {
var blurtData = null;
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
}
And do this in your componentDidMount
componentDidMount(){
blurtapi.getBlurts().then((data)=>{
this.setState({data})
}
}
In your example return blurtData; line will run synchronously, before the promise is resolved.
Modify getBlurts as:
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((data) => {
return data;
});
}
And in componentDidMount:
componentDidMount() {
getBlurts.then((data) => {
// data should have value here
});
}
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => return res)
async componentDidMount() {
const response = await blurtapi.getBlurts();
}
or
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
componentDidMount() {
const data = blurtapi.getBlurts()
.then(data => {
// Do something or return it
return data;
});
}
In my componentDidMount function I call AsyncStorage to get some saved value and then make a GET request and fetch data like below:
componentDidMount() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:' + value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson,
isLoading: false,
getValue: value
})
})
.catch((Error) => {
console.log(Error)
})
})
}
Now, I need to make another GET request. Suppose if I want to make the same request again in this function , how can I do that?
I solved it very easily from the suggested comments. I did the API call part in two different functions and then called these two functions inside ComponentDidMount like below code-
getFirstApiResposnse() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:'+ value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type' : 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response)=> response.json() )
.then((responseJson) => {
this.setState({
dataSource: responseJson,
isLoading: false,
getValue: value
})
})
.catch((Error) => {
console.log(Error)
});
}
)
};
getSecondApiResponse() {
AsyncStorage.getItem("token").then(value => {
const url = 'my url';
console.log('token:'+ value)
return fetch(url, {
method: 'GET',
headers: new Headers({
'Content-Type' : 'application/json',
'token': 'abcd',
'jwt': value
})
})
.then((response)=> response.json() )
.then((responseJson) => {
console.log('####:'+responseJson.cat_note)
this.setState({
isLoading: false,
getValue: value,
})
})
.catch((Error) => {
console.log(Error)
});
}
)
}
componentDidMount() {
this.getFirstApiResponse();
this.getSecondApiResponse();
}
You can also use Promise.all(). Which comes handy with multiple requests. Also, we can use helper library such as async and use its forEach, waterFall, series, parallel, etc methods depending on project needs. These things make our code more readable and scalable.
I need to return the result of a function from another page in react native which performing a fetch call. I use the method as follows. As I know this is because asynchronous call. Is there a special way to achieve this in react native ?
fetchcall.js
import address from '../actions/address'
const dashboard = {
getvals(){
return fetch(address.dashboardStats(),
{method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify( {...
}),
})
.then((response) => response.json())
.then((responseData) => {
console.warn(responseData);
return responseData;
})
.catch((error) => { console.warn(error); })
.done();
// return 'test_val'';
}
}
export default dashboard;
dashboard.js
import dashboard from '../../services/dashboard';
class Dashboard extends Component {
componentDidMount(){
console.warn(dashboard.getvals());
}
}
export default connect(mapStateToProps, bindAction)(Dashboard);
Its display the result as "undefined", but that fetch call works and it displays the result. Any suggestion?
In fetchcall.js you are returning a Promise. Also since you are returning the responseData in the .then() method itself, you don't need the .done() method.
Since getvals() is returning a Promise, you need to access it's value in a .then() method.
Overall, your code should be like this:
function getvals(){
return fetch('https://jsonplaceholder.typicode.com/posts',
{
method: "GET",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
.then((response) => response.json())
.then((responseData) => {
console.log(responseData);
return responseData;
})
.catch(error => console.warn(error));
}
getvals().then(response => console.log(response));
The best architectural pattern, I think, is to use a callback function, usually by writing in as an anonymous function.
///Component.js
my_service.login((data=>{
this.setState({body: data.body});
}));
////Service.js
export const login = function (cb){
fetch('http://myapi.com/103?_format=hal_json')
.then((response) =>{
return response.json();
})
.then((data) =>{
cb(data);
});
}
I am still a junior developer, but use this pattern frequently. If someone has a reason for a different approach, I would love to hear it.
fetch always return a Promise doesn't matter what you are returning.
so you can return a string, variable or something else but you have to use .then to access data
file where you make fetch request
export const graphql = () => {
return fetch("https://graphqlzero.almansi.me/api", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify({
query: `{
user(id: 1) {
id
name
}
}`
})
})
.then((res) => res.json()).then((responseData) => {
console.log(responseData);
return responseData;
})
}
file where you call function
graphql()
.then((e) => {
console.log("data", e)
});