Re-request on state change react - reactjs

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

Related

How to wait for the promise to resolve and update the state

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.

React passing a variable object to setState vs. an anonymous object to setState

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;

Axios auto refresh every 60 seconds with Reactjs

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

How to use axios.all or promise.all to call all ajax calls just once? React

I have some AJAX calls using axios, and I want to change the state of all only when the last axios call is finished, I tried to use axios.all but I could not, could anyone help me?
The call:
export default class TeamStatus extends React.Component {
state = {
updated2018: "",
updated2017: "",
updated2016: "",
totalSkills: "",
totalNotUpdated: "",
}
componentWillReceiveProps(props) {
const firstName = localStorage.getItem('nameLoggedUser');
const lastName = localStorage.getItem('lastNameLoggedUser');
const fullName = `${firstName} ${lastName}`.toLowerCase();
const loggedUserIs = localStorage.getItem("user-role");
if (loggedUserIs === 'full') {
axios.get(`/api/wfmskills/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
totalSkills: res.data.count
})
})
axios.get(`/api/notupdated/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
totalNotUpdated: res.data.count
})
})
axios.get(`/api/updated2017/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2017: res.data.count
})
})
axios.get(`/api/updated2016/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2016: res.data.count
})
})
axios.get(`/api/updated2018/${props.managerStatusFiltered}/${props.cityStatusFiltered}/${props.countryStatusFiltered}/${props.squadNameStatusFiltered}/${props.domainStatusFiltered}/${props.subdomainStatusFiltered}`)
.then(res => {
this.setState({
updated2018: res.data.count,
});
})
}
}
So, i have 5 calls, and i want to change the state (update2018,2017,2016,totalSkills and notUpdate) when the last to finish (api/update2018)
Someone could help me? PLEASE???
Here is a quick way to wrap all your promises in 1 promise all, and then when all of them complete, it will trigger 1 setState. This will allow you to execute code after all 5 are done completing.
export default class TeamStatus extends React.Component {
state = {
updated2018: "",
updated2017: "",
updated2016: "",
totalSkills: "",
totalNotUpdated: "",
}
componentWillReceiveProps(props) {
const firstName = localStorage.getItem('nameLoggedUser');
const lastName = localStorage.getItem('lastNameLoggedUser');
const fullName = `${firstName} ${lastName}`.toLowerCase();
const loggedUserIs = localStorage.getItem("user-role");
if (loggedUserIs === 'full') {
const { managerStatusFiltered, cityStatusFiltered, countryStatusFiltered, squadNameStatusFiltered }
Promise.all([
axios.get(`/example1`),
axios.get(`/example2`),
axios.get(`/example3`),
axios.get(`/example4`),
axios.get(`/example5`),
]).then(([res1, res2, res3, res4, res5]) => {
this.setState({
totalSkills: res1.data.count,
totalNotUpdated: res2.data.count,
updated2017: res3.data.count,
updated2016: res4.data.count,
updated2018: res5.data.count
});
});
}
}
}

trying to NOT get response one change late

So I have an input :
onChange this input's value get's updated and stored in props for persistance but that's unrelated to the issue.
I'm going to add a checkmark next to the field if the entered value is found in the database.
I already have the call to API and answer set up (my redux reducer) :
import EventTypes from '../EventTypes';
const initialState = {
response: {},
};
export default (state = initialState, action) => {
switch (action.type) {
case EventTypes.EXISTS_FULFILLED.type:
console.log(action.payload.body().data());
return { ...state, response: action.payload.body().data() };
default:
return state;
}
};
Note that the above console log correctly prints the true/false value (+ sent value. I get the two back from the server).
but in the component that calls this :
const defaultProps = {
onChange: () => {
},
value: { type: '' },
provider: { type: '' },
};
class InputsComponent extends Component {
onChange = () => {
this.props.onChange(this.props.id);
};
updateFirstInput(event) {
const { onChange, exists, response } = this.props;
const payload = {
objectType: this.props.placeholder.toLowerCase(),
objectName: event.target.value,
};
exists(payload);
this.setState({ firstInput: event.target.value });
onChange({ value: {
...this.state,
firstInput: event.target.value,
} }, this.props.id);
setTimeout(() => {
this.setState({ exitsStore: response });
}, 200);
setTimeout(() => {
console.log(this.state.exitsStore);
}, 1000);
}
The console logs are empty the first time "updateFirstInput()" is called and then are always one "onChange" behind from being accurate.
that is for "hello" :
[input value] [ console log of value for which presence on the server is true/false]
"h" ""
"he" "h"
"hel" "he"
"hell" "hel"
"hello" "hell"
after awhile I figured out that this was happening.
I've tried this :
const defaultProps = {
onChange: () => {
},
value: { type: '' },
provider: { type: '' },
};
class InputsComponent extends Component {
onChange = () => {
this.props.onChange(this.props.id);
};
checkExists(object){
const { exists, response } = this.props;
const payload = {
objectType: this.props.placeholder.toLowerCase(),
objectName: object,
};
exists(payload);
return response;
}
updateFirstInput(event) {
const { onChange } = this.props;
this.setState({ firstInput: event.target.value });
onChange({ value: {
...this.state,
firstInput: event.target.value,
} }, this.props.id);
const response = await this.checkExists(event);
console.log(response);
this.setState({ exitsStore: response });
console.log('stored data :');
console.log(this.state.exitsStore);
}
but for some reason people online suggesting we use await are not providing context. the above is not even compilable. (npm start will fail)
and even before that ESLint indicates that "await is a reserved word expecting newline or semicolon".
but I'm quite sure the above even with correct syntaxe wouldn't work either.
it's probably not await but how do I manage to have a filled in response from server (ideally) within a single function, the same function that calls the input update.
Constrainst :
as far as I know you can't have both onBlur and onChange on the same input so since i'm already using onChange no onBlur.
and I can't call any of this within a parent or other : there are multiple input fields as children and I need to have the value check and checkmark appear actions happen within the child to be able to match the values together.
UPDATE :
and if I simply remove the timers :
const defaultProps = {
onChange: () => {
},
value: { type: '' },
provider: { type: '' },
};
class InputsComponent extends Component {
onChange = () => {
this.props.onChange(this.props.id);
};
updateFirstInput(event) {
const { onChange, exists, response } = this.props;
const payload = {
objectType: this.props.placeholder.toLowerCase(),
objectName: event.target.value,
};
exists(payload);
this.setState({ firstInput: event.target.value });
onChange({ value: {
...this.state,
firstInput: event.target.value,
} }, this.props.id);
this.setState({ exitsStore: response });
console.log(this.state.exitsStore);
}
...TWO calls behind.
setState is asynchronous. It looks like you are using timeouts to try to get around this fact, but that's a very crude and brute force way to "solve" the problem and it doesn't actually solve the problem (as shown by your results).
But setState has handlers available when you need to run code "after" it. You do this simply by passing in a callback function as the second argument. E.g.;
this.setState({
exitsStore: response
}, () => {
console.log(this.state.exitsStore);
});
The handy pre-existing methods of React to the rescue.
In this case : componentWillRecieveProps().
componentWillReceiveProps(newProps) {
const response = newProps.response;
const old = this.props.response;
console.log(response);
const id = this.props.id;
if (response !== old && response.objectIdentifier === id) {
if (response.activ) {
if (response.isthere) {
this.setState({ inputIcon: 4 });
} else {
this.setState({ inputIcon: 3 });
}
} else {
this.setState({ inputIcon: 2 });
}
}
}
onChange = () => {
this.props.onChange(this.props.id);
};
updateInput(event) {
const { onChange, exists, response } = this.props;
const inputValue = event.target.value;
this.setState({ input: inputValue });
onChange({ value: {
...this.state,
input: inputValue,
} }, this.props.id);
if (inputValue === '') {
this.setState({ inputIcon: 0 });
} else {
const placeHolder = this.props.placeholder.toLowerCase();
const objectIdentifier = this.props.id;
const payload = {
objectType: placeHolder,
objectName: inputValue,
objectIdentifier,
};
exists(payload);
this.setState({
existStore: response,
};
}
}
the reason why this works is that componentWillRecieveProps() by default receives a parameter which is the updated props state and this one is indeed the new one. you can check that there has indeed been an update by doing as I did : checking the object you have in props against the passed parameter and only perform your actions if they are different.
I need edits on this post please, because I'm terrible at expressing this with the correct developer terms!

Resources