How to fix Maximum update depth exceeded Error occuring in ComponentWillMount? - javascript

I have searched this issue, but related cases to this issue are not same as mine.
I have React client side where Im using react-big-scheduler. In ComponentDidMount I have to do API axios calls and set state with response data.
componentWillMount() {
let today = moment().format(DATE_FORMAT);
let schedulerData = new SchedulerData(today, ViewTypes.Week);
schedulerData.localeMoment.locale('en');
const token = localStorage.getItem("jwttoken");
axios.post('http://localhost/Auth/api/validate.php', {
"jwt": token
}, {
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
}
})
.then(response => {
console.log(response.data);
this.setState({
vendor: response.data.data
})
const vid = response.data.data.VendorID;
return axios.get('http://localhost/Auth/api/show_all_HR.php?VendorID=' + vid, {
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
}
})
.then(response => {
console.log(response.data);
schedulerData.setResources(response.data);
this.setState({
AllHR: response.data,
viewModel: schedulerData
});
console.log("I should be rendered at 1st", this.state.AllHR);
console.log(vid);
});
});
axios.get('http://localhost/Auth/api/show_one_HR_hours.php?VendorID=48', {
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
}
})
.then(response => {
console.log(response.data);
schedulerData.setEvents(response.data);
this.setState({
AllSlots: response.data,
viewModel: schedulerData
});
});
}
Now I get this Error for doing setState:
Maximum update depth exceeded. This can happen when a component
repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops.
Now All other solutions suggest that problem occurs when we call the function in onClick or put the () after function. But I have no such thing.
So Please suggest me what to change in my code?
EDIT:
I render it like this:
render() {
const {viewModel} = this.state;
return (
<div>
<Row style={ {width: "100%"}}>
<h3> Schedule Maintenance Service Time Slots</h3>
<Scheduler schedulerData={viewModel}
prevClick={this.prevClick}
nextClick={this.nextClick}
onSelectDate={this.onSelectDate}
onViewChange={ this.onViewChange}
eventItemClick={this.eventClicked}
viewEventClick={this.ops1}
viewEventText="Ops 1"
viewEvent2Text="Ops 2"
viewEvent2Click={this.ops2}
updateEventStart={this.updateEventStart}
updateEventEnd={this.updateEventEnd}
moveEvent={this.moveEvent}
newEvent={this.newEvent}
leftCustomHeader={leftCustomHeader}
toggleExpandFunc={this.toggleExpandFunc}
/>
</Row>
</div>
);
}
prevClick = (schedulerData) => {
schedulerData.prev();
schedulerData.setEvents(this.state.AllSlots);
this.setState({
viewModel: schedulerData
})
}
nextClick = (schedulerData) => {
schedulerData.next();
schedulerData.setEvents(this.state.AllSlots);
this.setState({
viewModel: schedulerData
})
}
onViewChange = (schedulerData, view) => {
schedulerData.setViewType(view.viewType, view.showAgenda, view.isEventPerspective);
schedulerData.setEvents(this.state.AllSlots);
console.log("Im changing VIEW");
}

Related

How can I call a function only once in typescript

I have been trying to start using typescript for react native but it has not been easy ride so far, finding it difficult to perform common function like fetch an api once on screen load, below is the current function am using but I dont want to use time outs plus its not even working properly(it has to wait for the actual time I set before it runs and its bad for android), I would like to call the function only once when the page loads, I have also tried using if else statements but I dont know why it not just working on typescript.
(function(){
setTimeout(function () {
const newsUrl = 'http://172.20.10.4:8000/mobile/news';
return fetch(newsUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
})
.then((response) => response.json())
.then((data) => {
let newArr = [...data];
setProducts(newArr);
//console.log(data);
alert(38783763);
})
.catch((error) => {
alert(error);
console.error(error);
});
}, 200);
}.call(this));
You can use the useEffect hook and pass an empty array the fetch data once on load.
useEffect(() => {
fetch(newsUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
})
.then((response) => response.json())
.then((data) => {
let newArr = [...data];
setProducts(newArr);
})
}, []);

FETCH helper function returns undefined when calling setState()

I have a helper function that uses FETCH to pull data from an API. I'm trying to set the response of the fetch request to my state using setState() but it keeps returning undefined. Here is my helper function: (I realize I'm exposing my keys on the front end but I will adjust if I ever deploy to production)
const authCode = {
async getAuthCode() {
const authCodeMatch = window.location.href.match(/code=([^&]*)/)[1];
await fetch(`https://id.twitch.tv/oauth2/token?client_id=${TWITCH_ID}&client_secret=${TWITCH_SECRET}&code=${authCodeMatch}&grant_type=authorization_code&redirect_uri=${TWITCH_CB}`, {
method: 'POST',
mode: 'cors',
'Access-Control-Allow-Origin':'*',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
})
.then(response =>
response.json()
)
.then(data => {
console.log(data.access_token);
fetch(`https://api.twitch.tv/helix/users?id=43691`, {
method: 'GET',
mode: 'cors',
'Access-Control-Allow-Origin':'*',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Client-ID': TWITCH_ID,
'Authorization': 'Bearer ' + data.access_token
}
})
.then(response => response.json())
.then(newdata => {
console.log(newdata);
if (!newdata.data) {
return [];
}
//console.log(newdata.data[0].view_count);
return newdata.data.map(views => ({
view_count: views.view_count
}))
})
})
}
Everything logs to the console fine so I'm pretty sure I'm mapping through the correct array to parse the data. In any case, here is the returned object in console as well as my component code. In my profile component it just pushes undefined to my state:
// Console response
data: Array(1)
0:
broadcaster_type: "partner"
description: ""
display_name: "Faker"
id: "43691"
login: "faker"
offline_image_url: "https://static-cdn.jtvnw.net/jtv_user_pictures/7d76e34d-3ee3-42c0-a46b-e320d37fcde6-channel_offline_image-1920x1080.jpeg"
profile_image_url: "https://static-cdn.jtvnw.net/jtv_user_pictures/b2eb8a23-d6ad-45aa-bd8e-062133b64452-profile_image-300x300.png"
type: ""
view_count: 81769581
__proto__: Object
length: 1
__proto__: Array(0)
Component
export default class Profile extends Component {
constructor(props) {
super(props);
this.state = {
viewData: []
}
this.handleNewPost = this.handleNewPost.bind(this);
}
handleNewPost () {
authCode.getAuthCode().then(viewcountResults => {
this.setState({viewData: viewcountResults})
})
}
render() {
return (
<div>
<Container>
<div className='mt-5'>
<Button color='success' size='lg' onClick={this.handleNewPost}>
Post Data
</Button>
</div>
<div>
{this.state.viewData.view_count}
</div>
</Container>
</div>
)
}
Thanks for any help!
Your function getAuthCode isn't returning a Promise containing the data you're wanting to receive. Also, you can greatly clean up that code by using await instead of a mixture of await and callbacks.
const authCode = {
async getAuthCode() {
const authCodeMatch = window.location.href.match(/code=([^&]*)/)[1];
let response = await fetch(`https://id.twitch.tv/oauth2/token?client_id=${TWITCH_ID}&client_secret=${TWITCH_SECRET}&code=${authCodeMatch}&grant_type=authorization_code&redirect_uri=${TWITCH_CB}`, {
method: 'POST',
mode: 'cors',
'Access-Control-Allow-Origin':'*',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log(data.access_token);
response = await fetch(`https://api.twitch.tv/helix/users?id=43691`, {
method: 'GET',
mode: 'cors',
'Access-Control-Allow-Origin':'*',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Client-ID': TWITCH_ID,
'Authorization': 'Bearer ' + data.access_token
}
});
const newData = await response.json();
console.log(newdata);
if (!newdata.data) {
return [];
}
return newdata.data.map(views => ({
view_count: views.view_count
}));
}
}

How to import external data fetch api into react componentDidMount method

I'm trying to consolidate some code in one of my react components because my componentDidMount method is getting a bit verbose. This gave me the idea to create an api that does all of my data fetching for the entire app.
I'm having an asynchronous issue I'm not sure how to resolve.
I created the separate api file (blurt.js):
exports.getBlurts = function() {
var blurtData = null;
fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((data) => {
blurtData = data;
});
return blurtData;
}
and imported it to my (.jsx) component via
import blurtapi from '../api/blurt.js';
The problem is that when I call blurtapi.getBlurts() within componentDidMount(), the value comes back as null. However, if I write the data to the console like so:
.then((data) => {
console.log(data);
});
all is as it should be. So, the function is returning before the db operation completes, hence the null value. How would I reign in the asynchronous aspect in this case? I tried an async.series([]) and didn't get anywhere.
Thanks
So fetch returns a promise, which it is async , so any async code will run after sync code. so this is the reason you get null at first.
However by returning the async function , you are returning a promise.
Hence this code:
exports.getBlurts = async () => {
const data = await fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
const jsonData = await data.json();
return jsonData;
}
To retrieve any promise data, you need the then function,
so in your componentDidMount, you will do:
componentDidMoint() {
blurtapi.getBlurts()
.then(data => console.log(data)) // data from promise
}
Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
async/await:
https://javascript.info/async-await
I hope this makes sense.
fetch call returns a promise. therefore in your function u do something like this
exports.getBlurts = function() {
var blurtData = null;
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
}
And do this in your componentDidMount
componentDidMount(){
blurtapi.getBlurts().then((data)=>{
this.setState({data})
}
}
In your example return blurtData; line will run synchronously, before the promise is resolved.
Modify getBlurts as:
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then((data) => {
return data;
});
}
And in componentDidMount:
componentDidMount() {
getBlurts.then((data) => {
// data should have value here
});
}
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => return res)
async componentDidMount() {
const response = await blurtapi.getBlurts();
}
or
exports.getBlurts = function() {
return fetch('/getblurts/false', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
componentDidMount() {
const data = blurtapi.getBlurts()
.then(data => {
// Do something or return it
return data;
});
}

How to re-render list automatically when form is submitted in React.js

I just figured out how to get and post data to database. The data is displayed in a list when page renders. I want the list to re render when the form input is submitted. As of now I have to refresh the page manually in order to get the new item to show in the list.
I have already tried to create a state change in the handleSubmit function. Nothing seems to happen though.
handleSubmit(event) {
event.preventDefault();
fetch('http://localhost:5000/api/listitems', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( {
//ID : this.state.id,
Content: this.state.content,
List_Group: this.state.listgroup,
Date_Added: this.state.date
})
})
.then(res => res.json())
.catch(err => console.log(err));
}
Below is the form and list code
<h1>Submit an Item</h1>
<form onSubmit={this.handleSubmit} style={formStyle}>
<label>
Content:
<input type="text" value={this.state.content} onChange=
{this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<h1>Grocery List</h1>
{this.state.items.map(
(item, i) =>
<p key={i}>{item.List_Group}: {item.Content}</p>
)}
The list should show the new item on submit automatically.
Keeping in mind I have zero practical knowledge of react, it seems to me you simply have to update the this.state.items array.
I assume that your component has some sort of loadItems method, so I would say that your then should look something more like .then(() => this.loadItems())
UPDATED:
//fetch data from server
handleData = () => {
fetch('http://localhost:5000/api/listitems')
.then(response => response.json())
.then(data => this.setState({ items: data }));
}
//call handleData() on page load
componentDidMount() {
this.handleData();
}
//this function fires when form is submited
handleSubmit(event) {
event.preventDefault();
fetch('http://localhost:5000/api/listitems', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( {
Content: this.state.content,
List_Group: this.state.listgroup,
Date_Added: this.state.date
})
})
.then(res => res.json())
.then(() => this.setState({content: ''}))
//call handleData() when form is submited, which reloads list
.then(() => this.handleData())
.then(console.log(this.state.items))
.catch(err => console.log(err));
}
So the handleData() fetched the data from the server. It is called in componentDidMount() for the initial page load. Then it is called again each time the handleSubmit() function is called by the form. Each time handleData() is called it re-renders the list.
Set the state of your list of items after getting the response.
here i am assuming that you are getting the whole list in response.
handleSubmit(event) {
event.preventDefault();
fetch('http://localhost:5000/api/listitems', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( {
//ID : this.state.id,
Content: this.state.content,
List_Group: this.state.listgroup,
Date_Added: this.state.date
})
})
.then(res => res.json())
.then(data => this.setState({items : data})
.catch(err => console.log(err));
}
You need to call this.setState. State updates trigger a rerendering.
handleSubmit(event) {
event.preventDefault();
fetch('http://localhost:5000/api/listitems', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( {
//ID : this.state.id,
Content: this.state.content,
List_Group: this.state.listgroup,
Date_Added: this.state.date
})
})
.then(res => {
// depending on the response you may need to extract the property here
this.setState({
items: res.json(),
});
})
.catch(err => console.log(err));
}
You may do this:
handleSubmit(event) {
event.preventDefault();
fetch('http://localhost:5000/api/listitems', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify( {
//ID : this.state.id,
Content: this.state.content,
List_Group: this.state.listgroup,
Date_Added: this.state.date
})
})
.then(res => res.json())
.then((items) => this.setState({items: items})) // keep in mind that you may have to transform items to appropriate format to display
.catch(err => console.log(err));
}

react native use navigation prop in fetch

I am pretty new to react native. I am using react navigation in my react-native app. I am passing some props from one screen to another, and I need to use one of the props in a fetch I am trying to execute within the componentDidMount lifecycle method. With everything I have tried, it sends the value for the "type" key, but it sends nothing for the "location" key (see code below). Could someone help me with what am I missing or doing wrong? I have tried several things to pass the prop but nothing has worked yet.
componentDidMount() {
const { params } = this.props.navigation.state;
var data = {
type: 'r',
location: params.location
}
return fetch('http://myapisite', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
}, function() {
// do something with new state
});
})
.catch((error) => {
console.error(error);
});
}
I was able to resolve the issue. I am not 100% certain why what I had didn't work, but when I changed it to the following, it worked as expected:
const {state} = this.props.navigation;
var data = {
type: 'restaurant',
location: state.params.location
}
return fetch('http://mylink', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}
)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson,
}, function() {
// do something with new state
});
})
.catch((error) => {
console.error(error);
});

Categories