where should i call the setintreval in react - reactjs

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!

Related

Unable to setState regardless of making setState synchronous

I am learning about how to use synchronous setState but it is not working for my project. I want to update the state after I get the listingInfo from Axios but it does not work, the res.data, however, is working fine
class ListingItem extends Component {
constructor(props) {
super(props);
this.state = {
listingInfo: {},
open: false,
};
this.getListingData(this.props.itemId);
}
setStateSynchronous(stateUpdate) {
return new Promise((resolve) => {
this.setState(stateUpdate, () => resolve());
});
}
getListingData = async (item_id) => {
try {
const res = await axios.get(`http://localhost:5000/api/items/${item_id}`);
console.log(res.data);//it's working
await this.setStateSynchronous({ listingInfo: res.data });
// this.setState({
// listingInfo: res.data,
// });
console.log(this.state.listingInfo);//no result
} catch (err) {
setAlert('Fail to obtain listings', 'error');
}
};
I would be really grateful for your help!
Thanks to #PrathapReddy! I used conditional rendering to prevent the data from rendering before the setState is done. I added this line of code on the rendering part:
render() {
if (Object.keys(this.state.listingInfo).length === 0) {
return (
<div>
Loading
</div>
);
} else {
return //put what you want to initially render here
}
}
Also, there is no need to modify the setState, the normal setState will do. Hope this is useful!

Is it possible to re-run componentDidMount when executing a function?

At the moment my componentDidMount retrieves jobs from the database and displays them on my main page.
I created a function called deleteJob, which deletes a Job. The problem is that I have to refresh the page to see the changes.
Is it possible to see the change immediately without refreshing the page? I thought this could be done if I somehow made componentDidMount run again when executing the function
Please let me know if this is possible.
componentDidMount() {
axios.get("/getJobs").then(result => {
console.log("appear!");
this.setState({ jobData: result.data }, () => {
console.log(this.state);
});
});
axios.get("/getServices").then(result => {
this.setState({ serviceData: result.data }, () => {
});
});
deleteJob() {
axios.get("/deleteJob/" + this.props.id).then(result => {
});
this.props.close()
}
Why do you see the change? Because it updates the state.
So you should update your state when you delete a job.
Better, extract the axios "getJobs" to a method of the class and then call it in componentDidMount, and in deleteJobs.
This way, you will refresh your state at each action.
What you can do in this case is fetch the jobs again after deleting the job, also you can separate your functions by their behavior so you can call them in different cases
componentDidMount() {
getJobs();
getServices();
}
getJobs() {
axios.get("/getJobs" + this.props.id).then(result => {
this.setState({ jobData: result.data }
});
}
getServices() {
axios.get("/getServices").then(result => {
this.setState({ serviceData: result.data }
});
}
deleteJob() {
axios.get("/deleteJob/" + this.props.id).then(result => {
getJobs(); // get jobs again after deletion
});
this.props.close()
}
You can just filter the state with the id which you are deleting
lets say
componentDidMount() {
axios.get("/getJobs").then(result => {
this.setState({ jobData: result.data }, () => {
console.log(this.state);
});
}
you can call a delete method on Click of a button
//Assuming the delete request is post Id
deleteJob =(id)=>{
deleteJob() {
axios.post("/deleteJob/" ,+ id)
//this will filter out the deleted job from existing state
let filteredJobs = this.state.jobData.filter(data=>data.id===id)
this.setState({
jobData:filteredJobs
)}
}
This will re-render the component without refreshing page and no API calls :)

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

React accessing state before ComponentDidMount

When I try to access a state variable which is set in ComponentDidMount, react throws an undefined error. This is because I believe when I'm calling the fetch api and setState in ComponentDidMount, the value isn't ready yet (async stuff). Is there a proper way to either delay the render until the setState call is done or some other way to get the state updated fully before render is called?
I think the code below will give you a basic idea how fetch data and render work.
class App extends Component {
state = {
data:{},
loading:true,
error:null,
}
componentDidMount = () => {
fetch('https://example.com/api/article')
.then((response) => {
return response.json();
})
.then((json) => {
this.setState({
data:json,
loading:false,
})
.catch(error => {
this.setState({
error,
loading:false,
})
});
});
}
render() {
const {data,error,loading} = this.state;
if(loading){
return "Loading ..."
}
if(error){
return "Something went wrong."
}
return 'your actual render component or data';
}
}
export default App;

what component lifecycle would you use to update data received from an api

state={url:'someurl' , stockData=[], error:false}
On initial mount i will grab the data from api and set it to state.
componentDidMount() {
this.getDataFromApi();
}
Function for grabbing the data and setting the state of stockData
getDataFromApi = () => {
fetch(this.state.url)
.then(res => res.json())
.then(
res => {
const sortedRes = res["results"].sort(function(a, b) {
return b.ask_price - b.bid_price - (a.ask_price - a.bid_price);
});
this.setState(
{
stockData: sortedRes
}
);
},
res => this.setState({ error: true })
);
};
This data is always changing how can I get real-time updates?
I tried using componentWillUpdate to compare current and next state and using setTimeOut with delay of 3 seconds so that it was not calling fetchData() so quickly but the delay was not working and fetchData was being called way too many times in a short amount of time.
componentWillUpdate(nextProps, nextState) {
if(nextState.stockData !== this.state.stockData) {
fetch(this.state.url)
.then(res => res.json())
.then(this.delay(function() {
res => {
const sortedRes = res["results"].sort(function(a, b) {
return b.ask_price - b.bid_price - (a.ask_price - a.bid_price);
});
console.log(sortedRes);
this.setState(
{
stockData: sortedRes
},
// () => console.log("SORTED RES", sortedRes)
);
},
res => this.setState({ error: true })
}, 3000)) // waits 3s
return true;
}
else {
return false;
}
}
The reason why setTimeout isn't working is because you aren't stopping each call from occurring. You're just delaying each one by 3 seconds. I suggest placing the entire Api call inside a setInterval in componentDidMount.
componentDidMount() {
this.apiCall = setInterval(() => {
this.getDataFromApi();
}, 3000)
}
Remember to do housekeeping when the component unmounts as well, or else your setTimeout will never stop, even when the component is removed from the DOM.
componentWillUnmount() {
clearInterval(this.apiCall);
}

Resources