React - state not updating after API call - reactjs

I am trying to setState after an API call, and I know this is an async task but I can't figure out how to update my state. My code looks like this:
loadUserDetails = () => {
this.setState({
isLoading: true,
status: "Fetching user details..."
}, () => {
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
this.setState({
userProfile: data
})
if (this.state.userProfile != null)
this.loadRolesData();
})
})
});
.catch(console.log);
}
The console logs are producing the correct values but when I try to update the userProfile to data it doesn't happen. Reading the docs I can see useEffect as a solution but unsure how to implement it.
Edit:
I am initiating this from componentDidMount(). I think this is the correct place but happy to be told otherwise.

I think you did the task in the wrong order.
Do fetch for the api, afterwards do setState. Here's one simple example.
fetch(...).then(res => {
this.setState({...})
})
Please don't get confused about the second parameter of setState, that is to wait till state to finish update. Normally that is designed for some special occasion, 99% of time you don't need that.

setState doesn't update the state immediately after the call, and so that's why there is a second argument (callback). It is fired only when the update is finished. You used that second argument in your first setState call actually. So you can either do the same thing in the second call:
this.setState({
isLoading: true,
status: "Fetching user details..."
}, () => {
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
this.setState({
userProfile: data
}, () => {
// this code will get fired only after the state updates
if (this.state.userProfile != null) {
this.loadRolesData();
}
})
})
.catch(console.log);
});
Or you can use react hooks which would require you to refactor your component into a function and rewrite your fetch logic like the following:
const [userProfile, setUserProfile] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
// This function will get fired every time the userProfile state updates
React.useEffect(() => {
if (userProfile != null) {
loadRolesData();
}
}, [userProfile]);
const loadUserProfile = () => {
setIsLoading(true);
setStatus("Fetching user details...");
fetch('url', { method: 'Get', credentials: 'include' })
.then(res => res.json())
.then((data) => {
console.log("results")
console.log(data.name);
console.log(data.surname);
console.log(data.emailAddress);
setUserProfile(data);
setIsLoading(false);
})
.catch(console.log);
};

Related

How to stop useEffect from reloading my page every time?

For some reason my whole page reloads every time it updates the state after it gets it from the database. The page flickers and I end up at the top of the page. Why is this?
I update the entire state in other functions like sort(), that works perfect without reloading. I have put event.preventDefault() in every click handler so that shouldn't be the problem.
One of the great things with using React is to have a smooth UI without reloading so this is annoying.
function App() {
const [contacts, setContacts] = useState({ items: [] });
useEffect(() => {
axios
.get('http://localhost:5000/')
.then((result) => {
setContacts({ items: result.data });
})
.catch((err) => console.log(err));
}, []);
And this is the function that gets called:
const handleSubmit = (event) => {
event.preventDefault();
if (!id) {
axios
.post('http://localhost:5000/add/', input)
.then(() => {
setInput(emptyState);
})
.catch((err) => console.log(err));
} else {
axios
.post(`http://localhost:5000/update/${id}`, input)
.then(() => {
props.updateContact(input);
setInput(emptyState);
})
.catch((err) => console.log(err));
}
window.location = '/';
};
You need to put something in your [].
You can see that we passed props.name into the array in the second argument. This will now cause the effect to always run again when the name changes.
If you don't pass anything it will always update and will be useless.
useEffect(() => {
document.title = `Page of ${props.name}`
}, [props.name])

How to send updated state in axios in React?

I am trying to send post request using axios in Reactjs.
I have two component a timer component and App component and in App component i am trying to submit a form and send an axios call when i fetch the time from Timer component and save itinto counter state
I have written a condition if counter is true then update my state and then further send the post request
Working Demo
here is a handle submit code:
const handleSubmit = e => {
console.log("handleSubmit");
e.preventDefault();
if (counter) {
console.log(counter);
const url = `url string`;
setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
});
console.log(state);
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
}
};
The problem is when counter is true its not update the state which causes error while send axios request.
I have consoled each and every thing but still it fails.
It seems there is lot of rendering.
If you are using class components, you can make the reuqest after the state has been set. Something like this:
this.setState({
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
}, () => {
axios
.post(url, state)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
});
Since you did set the react-hooks tag, I guess that approach is not what you need. In your case, I suggest saving new state in some temporary variable and than passing that variable to axios. Like this:
const newState = {
...state,
lastn: {
attestedTime: myDateFunc(),
time: counter
}
};
setState(newState);
axios
.post(url, newState)
.then(response => {
console.log(response);
console.log(response.data);
})
.catch(error => {
console.log(error);
});
setState can be executed asynchronously by React, to optimize the rendering process. For cases like this one, it can also take a callback function that is guaranteed to be executed after updating the state.
For example:
this.setState({
name:'value'
},() => {
console.log(this.state.name);
});
in this case console.log will be executed after setting the name variable.
see the docs: https://reactjs.org/docs/react-component.html#setstate

Assigning return values from api to state in React

Hi I am trying to call an api assign the returned values to a state object in React, the API is returning values but the values are not being set to state, not understanding what's the reason thank you
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then((response) => {
return response.json();
})
.then(data => {
filesFromApi = data.map(file => { return { value: file, display: file } });
}).catch(error => {
console.log(error);
debugger;
});
console.log(filesFromApi);
this.setState({
files: filesFromApi.map(file => {
return {
fileName: file,
checked: false
};
})
});
};
fetch is an async method. An async method dispatches an action with the callbacks and unblocks following code branch from executing. The callbacks are then used to act on completion (success or failure) of the async method execution.
As you are calling the setState outside of the callbacks of the fetch call's chain, it's not guaranteed to run after the fetch call is done. As Sudheer has pointed out in their comment, you should try to set the state in a then block of the fetch chain.
warning: untested code
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then(response => response.json())
.then(data => {
filesFromApi = data.map(file => ({ value: file, display: file });
this.setState({
files: filesFromApi.map(file => ({
fileName: file,
checked: false
})
})
});
}).catch(error => {
console.log(error);
debugger;
});
};

Integrating two react functions in a synchronous way

I have the React code below. I want integrate the handleUpdate into the handleUpload function in a synchronous way so that the state is set before the rest of the function is executed. I tried, the following below, with my edit in bold, but it appears to be executing asynchronously and not setting state before executing. Can anybody please instruct me where i need to make a change to meet my needs?
handleUpdate = event => {
this.setState({
selectedFile: event.target.files[0]
})
}
handleUpload = () => {
**this.handleFileChange;**
var fd = new FormData();
fd.append('file', this.state.selectedFile, this.state.selectedFile.name);
fetch('/upload', {method: 'POST', body: fd})
.then(response => {
return response.json()})
.then(result => {
console.log(result)})
.catch(function(error) {
console.log("ERROR:" + error);
});
}
handleUpdate = event => {
this.setState({
selectedFile: event.target.files[0]
}, ()=>this.handleUpload())
}
You can call the handleUpload function inside the callback function of setState, to make sure that the handleUpload function gets called only after the state.selectedFile is set

ReactJS setState when all nested Axios calls are finished

I have a problem with updating my state from nested axios call inside forEach loop:
constructor(props) {
super(props);
this.state = {
isLoaded: false,
items: []
};
//Binding fetch function to component's this
this.fetchFiles = this.fetchFiles.bind(this);
}
componentDidMount() {
this.fetchFiles();
}
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
The idea is to get all items from Dropbox using it's API (JavaScript SDK)
and then for each item I also need to call different API endpoint to get a temporary download link and assign it as a new property. Only after all items will get their links attached I want to setState and render the component. Could somebody please help with this, I spend already multiple hours fighting with promises :S
You could use Promise.all to wait for multiple promises. Also keep in mind that setState is async and you wont see immediate changes. You need to pass a callback.
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
// wait for all nested calls to finish
return Promise.all(items.map((item, index) => {
return axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
return item
});
}));
})
.then(items => this.setState(prevState => ({
isLoaded: true,
items: items
}), () => console.log(this.state.items)))
.catch((error) => {
console.log(error);
})
}
Try making the fetchfiles() function as an asynchronous method by adding the async keyword.Now, we have to wait till the items to get their download link, so add a await keyword before that line which makes the code to wait till the axios call gets completed.
async function fetchFiles() {
axios.get('/list')
.then(async function(response){
var items = response.data.entries;
await items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
I haven't tested the code, but it should probably work.

Resources