ReactJS SetTimeout / setInterval [duplicate] - reactjs

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.

Related

React setstate does not work in IE unless timeout is used

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
});

React setstate callback does not work after initial use

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})
});
}

where should i call the setintreval in react

I am trying to call getSession() every 5sec of delay. But in initial render i would like to call this function and execute immediately.
According to my below code, in the initial render itself it is using the delay of 5sec to display the output.
How can i achieve the following:
1. Initial render should be done immediately
2. after every 5sec getSession() should be called as well.
Current Results:
It is taking 5sec delay to display in initial render.
Expected results:
Initial render should be done immediately.
componentDidMount() {
this.getSession();
}
getSession() {
var path = "Sharing.aspx/GetSessions";
setInterval(() => {
axios
.post(path, { withCredentials: true })
.then(response => {
let element = response.data.d;
this.setState({
sessions: element
});
})
.catch(error => {
this.setState({
Errors: error
});
console.error(error);
});
},5000
);
}
render() {
return (
<div>
{this.renderSessionDetails()}
</div>
);
}
Expected results:
Initial render should be done immediately.
After every 5sec getSessions() should be called.
I would do something like this:
const INTERVAL = 6000;
class Component extends React.Component {
componentDidMount() {
this.getSession();
this.intervalId = window.setInterval(() => this.getSession(), INTERVAL);
}
componentWillUnmount() {
window.clearInterval(this.intervalId);
}
getSession() {
var path = "Sharing.aspx/GetSessions";
setInterval(() => {
axios
.post(path, { withCredentials: true })
.then(response => {
let element = response.data.d;
this.setState({
sessions: element
});
})
.catch(error => {
this.setState({
Errors: error
});
console.error(error);
});
}, 5000);
}
render() {
return <div>{this.renderSessionDetails()}</div>;
}
}
ComponentDidMount will be called only once, and at that point, you call the first getSession call, and start the interval.
An important thing to bring attention to is the call to window.clearInterval when the component gets unmounted. This is to make sure that interval doesn't keep running eternally, and worst, that more than one interval run in parallel after having this component mount a couple of times.
I hope it helps.
You could go about refactoring your code to look like that, in order to avoid waiting initially for those 5 seconds. The refactor is mainly about extracting the fetching logic away from the timer implementation. Please note that inside componentDidMount() we first call this.getSession() immediately, which is fine because we eliminated the intervals from it. Then we dispatch the intervals.
class Component extends React.Component() {
intervalId = null
componentDidMount() {
this.getSession()
this.intervalId = setInterval(() => this.getSession(), 5000)
}
componentWillUnmount() {
if (this.intervalId) {
clearInterval(this.intervalId)
}
}
getSession() {
var path = 'Sharing.aspx/GetSessions'
axios
.post(path, { withCredentials: true })
.then(response => {
let element = response.data.d
this.setState({
sessions: element
})
})
.catch(error => {
this.setState({
Errors: error
})
console.error(error)
})
}
render() {
return <div>{this.renderSessionDetails()}</div>
}
}
I would also try to make sure we're not running into race conditions here. But, if you're sure your requests never take more than 5 seconds -- it should be fine. Hope it helps!

SetState and React-Native lifecycle

I'm taking my first steps with React-Native. I can not understand why with the following code I get the value "data" = [] inside _refreshData (console.log(this.state.data);)
I have this code from Learning React Native book:
class SimpleList extends Component {
constructor(props) {
super(props);
console.log("Inside constructor");
this.state = { data: [] };
}
componentDidMount() {
console.log("Inside componentDidMount");
this._refreshData();
}
...
_refreshData = () => {
console.log("Inside_refreshData");
console.log(NYT.fetchBooks());
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
console.log("This is data: ");
console.log(this.state.data);
};
function fetchBooks(list_name = "hardcover-fiction") {
console.log("Inside fetchBooks");
let url = `${API_STEM}/${LIST_NAME}?response-format=json&api-
key=${API_KEY}`;
return fetch(url)
.then(response => response.json())
.then(responseJson => {
return responseJson.results.books;
})
.catch(error => {
console.error(error);
});
}
Debugging (with console.log) I see that "data" = [] even if I just called the setState and from the log I see that the fetch returned my values ...
This is the call log:
Can you explain why please?
Thanks in advance.
Ok, first it's promise and asynchronous, and it's not guaranteed that when you log your data also you receive the data, so when you are in componentDidMount and call console.log(this.state.data); maybe the data is not returned yet. think it took 2000 milliseconds to return the data from api. so you call
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
and then this code as I said took 2000 milliseconds, but as I said you immediately log the data so, because at this time data is not filled you see the empty array.but if you want to see the data you can log it here :
NYT.fetchBooks().then(books => {
console.log(books);
this.setState({ data: this._addKeysToBooks(books) });
});

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