I'm using ReactJS as a javascript library and I am getting the data in componentDidMount() using axios. These received data must be taken again every 60 seconds. What is the most efficient and good way to do this?
componentDidMount() {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.map(() => {
const data = [];
res.data.map((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({ id: item.id, isyeri: item.isyeri });
});
newGteeChartSeries.push({ name: "GTEE", data });
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
}
One way of going about it is to move the data fetching logic to a separate method and create an interval that will invoke this method every 60 seconds.
Make sure you store the number returned from setInterval on the component instance so you can use clearInterval in componentWillUnmount.
Example
class MyComponent extends React.Component {
interval = null;
componentDidMount() {
this.interval = setInterval(this.getData, 60000);
this.getData();
}
componentWillUnmount() {
clearInterval(this.interval);
}
getData = () => {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.forEach(() => {
const data = [];
res.data.forEach((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({ id: item.id, isyeri: item.isyeri });
});
newGteeChartSeries.push({ name: "GTEE", data });
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
};
}
I would suggest abstracting the api request into its own function
componentDidMount(){
setInterval(yourApiCallFn(),60000)
}
You can wrap all in a function.
Call that function in ComponentDidMount(), and use setInterval(myFunction(), 60000) to call that function every 60 seconds
Below works without syntax error.Call that function without parenthesis
``componentDidMount() {
this.interval = setInterval( this.props.Details, 6000);
this.props.Details()
}
componentWillUnmount() {
clearInterval(this.interval);
}``
Well let's do that with a normal javascript setTimeInterval.
let intervalLoop = null; // a class variable
componentDidMount() {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
this.intervalLoop = setInterval(()=>{
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.map(() => {
const data = [];
res.data.map((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({
id: item.id,
isyeri: item.isyeri
});
});
newGteeChartSeries.push({
name: "GTEE",
data
});
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
}, 60000);
}
// need to cleanup the timeinterval whenever we destroy the component
componentWillUnmount(){
clearInterval(this.intervalLoop)
}
Related
in my case I want to implement the onSnapshot function for getting realtime updates, so here's my sample code it's working now:
db.collection("Cities").onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) => {
console.log(doc.data());
});
});
But now, how can I implement it to the following code
getMyInfo = () => {
db.collection("Cities")
.limit(8)
.get()
.then((docs) => {
if (!docs.empty) {
let AllCities = [];
docs.forEach(function (doc) {
const city = {
id: doc,
...doc.data(),
};
AllCities.push(city);
});
this.setState(
{
cities: AllCities,
},
() => {
this.setState({
isLoaded: true,
});
}
);
}
});
};
Thanks
You are saving all your cities into your state. Use it to add or update the new (or updated) cities.
Maybe you can try something like that :
db.collection("Cities").onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) => {
const updatedCity = doc.data();
const cities = [...this.state.cities];
const index = this.state.cities.findIndex(c => c.id === updatedCity.id);
if (index >= 0) {
cities[index] = updatedCity;
} else {
cities.push(updatedCity);
}
this.setState({ cities });
});
});
This is what my code looks like:
constructor(props) {
super(props);
this.state = {
docs: []
};
}
async componentDidMount() {
await this.quizes();
console.log(this.state.docs);
}
quizes = () => {
firebase
.firestore()
.collection("quiz")
.get()
.then(result => {
const docs = result.docs.map(doc => {
return { uid: doc.id, ...doc.data() };
});
this.setState({ docs });
});
};
Currently console.log(this.state) returns empty docs when I am trying to update it with documents from firestore.
setState is asynchronous. If you are sure that your collection is not empty then you can see your state using:
this.setState({ docs }, () => console.log(this.state);
The function as second argument of setState is run only when the asynchronous task of setting the state is done, thus you are going to see the updated state.
In order to await your quizes function it also needs to be async and use the await syntax rather than promises.
For example this code should achieve the desired outcome:
constructor(props) {
super(props);
this.state = {
docs: []
};
}
async componentDidMount() {
await this.quizes();
}
quizes = async () => {
let result = await firebase.firestore().collection("quiz").get()
const docs = result.docs.map(doc => {
return { uid: doc.id, ...doc.data() }
});
return this.setState({ docs }, () => {
console.log(this.state.docs);
});
};
EDIT:
setState uses a callback. In order to guarantee the state has been set at the time of logging, use callback within the quizes function.
I was having issues rendering a chart after state has been updated by the data fetched from an API.
import React, { Component } from 'react';
import { Bar, Line, Pie } from 'react-chartjs-2';
export default class PopChart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {
labels: [],
datasets: [
{
label: 'AAA',
data: [],
backgroundColor: []
}
]
}
};
}
url = 'api_url';
componentDidMount() {
fetch(this.url)
.then(response => {
this.setState({
httpStatusCode: response.status,
httpStatusOk: response.ok
});
if (response.ok) {
return response.json();
} else if (response.status === 404) {
// Not found
throw Error('HTTP 404, Not found');
} else {
throw Error(`HTTP ${response.status}, ${response.statusText}`);
}
})
.then(responseData => {
console.log('Then function');
this.setChartProperties(responseData);
})
.then(() => {
this.setState({ loading: false });
})
.catch(error => {
console.log(error);
});
}
setChartProperties = data => {
this.setChartData(data);
// this.setChartXAxis(data);
};
setChartData = data => {
let hours = data.map(event => event.hour);
hours = hours.splice(0, 10);
const { chartData } = { ...this.state };
const chartDataUpdate = chartData;
chartDataUpdate['datasets'].data = hours;
// Working version
// this.setState({
// chartData: {
// labels: ['a', 'a', 'a', 'a', 'a', 'a', 'a'],
// datasets: [
// {
// label: 'AAA',
// data: hours,
// backgroundColor: []
// }
// ]
// }
// });
// Not working version
this.setState({ chartData: chartDataUpdate });
};
render() {
<Bar data={this.state.chartData} />;
}
}
At first I thought the issue was regarding the asynchronous nature of fetch() and the <Bar/> component from Chart.js was being rendered before the state got a chance to get updated. However, when I logged out the state, on the console it showed my state with the changed values. However, the chart did not display any of the values.
When I called setState() and passed an anonymous object (refer to the commented out working version of setState()), the chart displayed the values.
Why does it not work when I stored the fetched data into a variable and pass it as an argument for setState()?
One thing I noticed is that state.chartData.datasets is an array, but in setChartData you're setting data on the array not one of its elements.
This:
chartDataUpdate['datasets'].data = hours;
Should be:
chartDataUpdate['datasets'][0].data = hours;
Im trying to make my component make a new request when state changes in react. I believe that i understand the lifecycle methods, but i cannot manage to make my component make a new request when the date state changes.
I have used componentWillUpdate with prevState, where prevState returns the updated state instead of the previous date.
Anything that can help me in the right direction is appriciated.
this.state = {
date: moment(),
bookings: []
}
componentWillUpdate(prevProps, prevState) {
// prevState returns updated state
}
componentWillMount() {
const { course } = this.props.match.params;
const payload = {
startDate: this.state.date.format('MM-DD-YYYY'),
endDate: this.state.date.add(1, 'd').format('MM-DD-YYYY')
}
axios.get(`/api/booking/find/${course}? startDate=${payload.startDate}&endDate=${payload.endDate}`).then(response => {
this.setState({
bookings: response.data
})
});
}
onNext = () => {
this.setState({
date: this.state.date.add(1, 'd')
})
}
onPrev = () => {
this.setState({
date: this.state.date.subtract(1, 'd')
})
}
According to React doc, UNSAFE_componentWillUpdate will soon be deprecated.
Btw, this function signature is UNSAFE_componentWillUpdate(nextProps, nextState), this is why you get the updated state.
You should consider extracting your axios get request inside a function, that could be called inside a componentDidUpdate lifecycle method :
this.state = {
date: moment(),
bookings: []
}
componentDidUpdate(prevProps, prevState) {
if (this.state.date.valueOf() !== prevState.date.valueOf()) {
this.getBookings();
}
}
componentWillMount() {
this.getBookings();
}
getBookings = () => {
const { course } = this.props.match.params;
const payload = {
startDate: this.state.date.format('MM-DD-YYYY'),
endDate: this.state.date.add(1, 'd').format('MM-DD-YYYY')
}
axios.get(`/api/booking/find/${course}? startDate=${payload.startDate}&endDate=${payload.endDate}`).then(response => {
this.setState({
bookings: response.data
})
});
}
onNext = () => {
this.setState({
date: this.state.date.add(1, 'd')
})
}
onPrev = () => {
this.setState({
date: this.state.date.subtract(1, 'd')
})
}
It sounds like you want componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if (!this.state.date.isSame(prevState.date)) {
// do request
}
}
I'm trying to use ComponentWillUnmount to stop listening to Firebase Firestore collection changes:
https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener
var unsubscribe = db.collection("cities")
.onSnapshot(function (){
// Respond to data
// ...
});
// Later ...
// Stop listening to changes
unsubscribe();
However, I cannot access this unsubscribe(); since it is declared inside ComponentWillMount and I need to use it in ComponentWillUnmount.
How can I use this unsubscribe() inside ComponentWillUnmount? If I try to save it inside the state it throws an error that unsubscribe is not a function.
constructor() {
super();
this.state = {
notes: [],
unsubscribe: null
};
this.getNotes = this.getNotes.bind(this);
}
componentDidMount(){
this.getNotes();
}
componentWillUnmount(){
var unsubscribe = this.props.unsubscribe;
unsubscribe();
}
getNotes = () => {
const db = this.props.firestore;
const colRef = db.collection("users").doc(this.props.uid)
.collection("notes");
let notes = [];
const that = this;
// Realtime updates listener
var unsubscribe = colRef.orderBy("timestamp", "asc")
.onSnapshot(function(querySnapshot) {
var notes = [];
querySnapshot.forEach(function(doc) {
notes.push(
{ id: doc.id,
body: doc.data().body}
);
});
that.setState({ notes })
});
this.setState({ unsubscribe })
}
Throws:
Uncaught TypeError: unsubscribe is not a function
You can save the unsubscribe reference on the class instance (this): instead of doing var unsubscribe do this.unsubscribe = [...] and later just read it from the class instance again: this.unsubscribe()
componentDidMount(){
this.getNotes();
}
componentWillUnmount(){
this.unsubscribe();
}
getNotes = () => {
const db = this.props.firestore;
const colRef = db.collection("users").doc(this.props.uid)
.collection("notes");
let notes = [];
const that = this;
// Realtime updates listener
this.unsubscribe = colRef.orderBy("timestamp", "asc")
.onSnapshot(function(querySnapshot) {
var notes = [];
querySnapshot.forEach(function(doc) {
notes.push(
{ id: doc.id,
body: doc.data().body}
);
});
that.setState({ notes })
});
}