I have below method where am making a service call using whatwg-fetch. setstate does not work with IE browser. It works fine with other browsers.
After i enclosed setstate within settimeout it works good in IE.
Not sure this timeout will affect once its deployed in prod servers and time delay for response gets increased. Please suggest me with an ideal solution for this issue. Thanks!
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
setTimeout(() => {
this.setState({
value: "edit",
items: result
});
}, 1000);
}
)
.catch(error => console.error('Error:', error));
}```
This may be because of the time delay in getting the result. You could put in a conditional statement before setting the state to check that a result has been received.
fetch("/local/addThings").then(res => res.json())
.then(
(result) => {
if (result) {
this.setState({
value: "edit",
items: result
});
}
} else {
setTimeout(() => {
this.setState({
value: "edit",
items: result
});
}, 1000);
}
)
.catch(error => console.error('Error:', error));
}
I also noticed you have two .then statements. What if you set the state with the first one like this?
fetch("/local/addThings")
.then(res =>
this.setState({
value: "edit",
items: res
})
)
.catch(error => console.error('Error:', error));
}
It may have to do with setState being asynchronous:
You can try to remove the setTimeout and give setState a function instead of an object, like this:
this.setState(() => ({
value: "edit",
items: result
}));
That's because calling setState() in React is asynchronous and it doesn't always immediately update the component. Please check the official documentation about setState().
You could use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. We just need to get the updated state in the callback:
this.setState({ value: "edit", items: result },()=>{
console.log(this.state.value); //any function u want to call after state changed
});
Related
This question already has answers here:
Adding setInterval to componentDidMount in React
(2 answers)
Closed 1 year ago.
I am trying to get the following to reload every 30 seconds.
componentDidMount() {
fetch('https://example.com/json')
.then((res) => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.data,
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error,
});
},
);
}
I have tried to wrap it in setInterval but no success. I am wondering how do I make this into a function so I can reload it as need be.
What I would suggest you is to take fetch function out of componentDidMount method. Write it inside a loadData() called function or whatever you want to name it. Then in loadData() function wrap your fetch logic with like
following.
const loadData = () => {
setInterval(
async function(){
//your fetch logic here with await
}, 30000);
}
componentDidMount() {
loadData()
}
Let's say you want to fetch data every 30 sec after you enter in component and then stop this fetching on component exit:
componentDidMount() {
this.myInterval = setInterval(() => {
fetch('https://example.com/json')
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.data
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: true,
error
});
});
}, 30000);
}
componentWillUnmount() {
clearInterval(this.myInterval);
}
Should be enough.
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);
};
I have a function that, when initialized, takes a previously set state and uses it to make an api call with axios:
_onRefresh = () => {
this.setState({ refreshing: true }, () => {
axios.get(this.state.currentPath)
.then(res=>{
console.log(res.data)
this.props.loadCards(res.data)
})
this.setState({refreshing: false})
});
}
I can see that the promise is never completed, and that a response is not given.
However, on the first use after the page loads, the function works correctly; it's only on subsequent usage that it does not work.
When the get request does not work, I've taken the path that's been stored in state, made a request in postman, and received a valid result.
you should cancel the refreshing in the finally block
get(...)
.then(...)
.catch(...)
.finally(() => this.setState({refreshing: false}))
Does this fix the issue?
_onRefresh = () => {
this.setState({ refreshing: true }, () => {
axios.get(this.state.currentPath)
.then(res=>{
console.log(res.data)
this.props.loadCards(res.data)
this.setState({refreshing: false}) // moved this line into the then block
})
});
}
Try this
_onRefresh =async () => {
this.setState({ refreshing: true }, () => {
await axios.get(this.state.currentPath) //you might need to handle promises to get this working.
.then(res=>{
console.log(res.data)
this.props.loadCards(res.data)
})
this.setState({refreshing: false})
});
}
Since my app is fetching images from API and rendering the result as expected. But showing this warning is incomplete to this project and given answers aren't solved out my issue.
Moreover, it couldn't be solved with AbortController to pass the signal as a parameter in fetch call and using AbortController.abort() in componentWillUnmount
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount
method.
CODE:
componentDidMount() {
this.getImage(Flikr_URL);
}
getImage(url) {
fetch(url)
.then(response => response.json())
.then(responseJson =>
this.setState({
imageData: responseJson.photos.photo,
loading: false
})
)
.catch(err => {
console.log(err);
throw error;
});
}
componentWillUnmount() {
this.getImage();
}
If you want simple solution, this will help you. May be another good solution will be there, but for now you can do like this.
Check manually component is mounted or not.
then in componentDidMount method set flag componentMounted to true.
componentDidMount() {
this.componentMounted = true;
this.getImage(Flikr_URL);
}
getImage(url) {
fetch(url)
.then(response => response.json())
.then(responseJson => {
if (this.componentMounted) { // check here component is mounted
this.setState({
imageData: responseJson.photos.photo,
loading: false
});
}
})
.catch(err => {
console.log(err);
throw error;
});
}
In componentWillUnmount method set flag to false
componentWillUnmount() {
this.componentMounted = false;
}
I am trying to update the setSet as part of output from my RestAPI. However I am getting an error that response object is undefined. I am able to log it outside setState method.
Code
addNewTodo = () => {
axios.post('http://localhost:5001/todos', "task="+this.state.newTodoList.task)
.then(response=>console.log(response.data))
.then(response=>{
this.setState(prevState=>({
TodoList: prevState.TodoList.push(response.data),
}))
});
{this.toggleNewTodoModal()}
}
I get following log in console before error
{task: "ddd", id: "todo10"}
Error:
TypeError: Cannot read property 'data' of undefined
at following line
TodoList: prevState.TodoList.push(response.data),
So your first .then returns a console log, meaning your second .then will no longer have any values. If you change your code to this:
Regarding pushing new Data to react state array, The recommended approach in later React versions is to use an updater function when modifying states to prevent race conditions. So pushing new Data to state array should be something like below
axios
.post('http://localhost:5001/todos', 'task=' + this.state.newTodoList.task)
.then(response => {
console.log(response.data);
this.setState(prevState => ({
TodoList: [...prevState.TodoList, response.data],
}));
});
It should work just fine. You can chain .then as much as you like, as long as you return some values, and not a console log, for example, in the fetch:
fetch('some_url', {
method: 'GET',
})
.then(res => res.json()) // this returns the data
.then(data => console.log(data)) // this has access to the data
My state object was a map, and so following worked for me.
State
state = {
TodoList: {},
}
Updating State
axios
.post('http://localhost:5001/todos', 'task=' + this.state.newTodoList.task)
.then(response => {
const {id, task} = response.data
this.setState(prevState => ({
TodoList: {...prevState.TodoList,
[id]: task},
}));
});