Maximum update depth exceeded ReactJs componentDidMount - javascript

I understood the error, my componentDidUpdate functions is creating an infinite loop, i don't know hot to fix it. I found the two errors that the traceback show me, but i don't know what to do. Here is the submit handler function, it's in the main (logup) component:
submitHandler = event => {
event.preventDefault();
const {month, day, year} = this.state.data;
this.setState({
loading: true,
error: false,
errors: {},
data: {
...this.state.data,
foundation: `${month} ${day} de ${year}`
}
});
fetch('http://127.0.0.1:8000/logup/',
{
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(this.state.data)
}).then(response => {
this.setState({ loading: false });
if (response.ok) {
console.log('redirect to social nets');
} else {
this.setState({ error: true });
}
return response.json();
}).then(json => {
if (this.state.error) {
this.setState({errors: json}) // The traceback give me error right here
} else {
console.log(json);
}
});
};
I also have many Inputs component in the render of that logup component, the traceback show me errors here too.
state = {
error: false
};
componentDidUpdate() {
let bad = Object.keys(this.context.errors).includes(this.props.name);
if (bad) {
this.setState({ error: true }); // traceback give me error too.
};
};

In your componentDidUpdate you update state in the following manner:
this.setState({ error: true })
You do so under the condition that this is true:
Object.keys(this.context.errors).includes(this.props.name)
Setting the state causes the component to rerender and also update, so componentDidUpdate will run again. However, it is very likely that when that happens your condition above will still be true. You will thus update state again, and React will not short circuit the state update because you are creating a new object every time. One simple way to save yourself here would be to change your condition to:
if (bad && !this.state.error) {
this.setState({ error: true }); // traceback give me error too.
};

You should use some type of conditions inside componentDidUpdate for not to trigger state update. Like thus below:
componentDidUpdate(prevProps, prevState) {
let bad = Object.keys(this.context.errors).includes(this.props.name);
if (prevState.error !== this.state.error && bad) {
this.setState({ error: true }); // traceback give me error too.
};
};
Always compare the prevState and currentState value inside componentDidUpdate as in most cases this condition is suffice.
Note : Before starts using componentDidUpdate please refer to the docs as it also provides prevProps and prevState values which
are always helpful in such scenarios.

Change
if (bad) {
this.setState({ error: true }); // traceback give me error too.
};
to
if (bad && !this.state.error) {
this.setState({ error: true }); // traceback give me error too.
};
Using setState inside of componentDidUpdate will cause it to re-render and recall componentDidUpdate. Adding an additional check will help stop this loop.

Related

JSON Object Property not returnable - "Cannot Read Property of Undefined"

Ok, so I'm building out a custom API in React. When I make the calls, I'm getting JSON data back and store that into local storage with JSON.Stringify:
localStorage.setItem('user', JSON.stringify(response.data))
Later, I call this item onto the Homepage to return some of that data once the user is logged in using:
var user = JSON.parse([localStorage.getItem('user')])
This returns the object:
{
"OrderId":0,
"IsLoggedIn":true,
"ModeOfSaleId":64,
"OriginalModeOfSaleId":64,
"SourceId":8580,
"LoginInfo":{"ConstituentId":190554,"OriginalConstituentId":190554,"UserId":"test#email.org","Status":"P","FailedAttempts":0,"LockedDate":null,"ElectronicAddress":"test#email.org"},
"CartInfo":{"PerformanceCount":0,"PackageCount":0,"ContributionCount":0,"MembershipCount":0,"UserDefinedFeeCount":0,"GiftCertificateCount":0,"PaymentCount":0,"FirstSeatAddedDateTime":null},
"BusinessFacing":false,
"IsGuest":false,
"CheckoutStatus":{"Status":"No Checkout","Date":null},
"HasLockedSeats":false,
"SeatsExpired":false
}
The Issue:
Un-nested properties return normally {user.OrderId} or {user.ModeOfSaleId} However, trying to return the nested values like {user.LoginInfo.ConstituentID} result in the error:
Uncaught TypeError: Cannot read property 'ConstituentId' of undefined
Returning {user.LoginInfo} actually returns an object, but obviously, can't print that to a string. Returning {user.LoginInfo["ConstituentId"]} results in the error:
Uncaught TypeError: Cannot read property 'ConstituentId' of undefined
So yeah, I'm stumped, I don't know how I'm returning this incorrectly. Any help is appreciated.
This code works for me:
localStorage.setItem("user", JSON.stringify({
"OrderId":0,
"IsLoggedIn":true,
"ModeOfSaleId":64,
"OriginalModeOfSaleId":64,
"SourceId":8580,
"LoginInfo":{"ConstituentId":190554,"OriginalConstituentId":190554,"UserId":"test#email.org","Status":"P","FailedAttempts":0,"LockedDate":null,"ElectronicAddress":"test#email.org"},
"CartInfo":{"PerformanceCount":0,"PackageCount":0,"ContributionCount":0,"MembershipCount":0,"UserDefinedFeeCount":0,"GiftCertificateCount":0,"PaymentCount":0,"FirstSeatAddedDateTime":null},
"BusinessFacing":false,
"IsGuest":false,
"CheckoutStatus":{"Status":"No Checkout","Date":null},
"HasLockedSeats":false,
"SeatsExpired":false
}));
const user = JSON.parse(localStorage.getItem("user"));
console.log(user.LoginInfo.OriginalConstituentId);
What about using spread operator to get what you want?
const user = JSON.parse(localStorage.getItem('user'))
const { ConstituentId, UserId } = user.LoginInfo
console.log(ConstituentId) // 190554
Ok, so the way I'm returning these values seems to be an "issue" because of the way React handles it's Render event. When I'm pulling in the data on the componentDidMount() event, a Render event still fires just before this.
componentDidMount() {
this.setState({
user: JSON.parse([localStorage.getItem('user')]),
users: { loading: true }
});
}
So in the Render event:
render() {
const { user, users, loading } = this.state;
var { ConstituentId, UserId } = user.LoginInfo
return (
<div className="col-md-6 col-md-offset-3">
<h1>Hi!</h1>
<p>{UserId}</p>
<p>You're logged in with React & Basic HTTP Authentication!!</p>
<h3>Users from secure api end point:</h3>
<p>
<Link to="/login">Logout</Link>
</p>
</div>
);
}
It fires TWICE, once BEFORE the state.user is set by componentDidMount() and once again after. So, my code was erroring out because of the first firing of Render, when nothing was set, hence the undefined message. I figured out how to bypass this with checking the login info object is returning as typeof object. This is in my render event:
var result = (typeof user.loginInfo === 'object');
if (result && loading) {
console.log(result)
console.log(user.LoginInfo.ConstituentId)
var { ConstituentId, UserId } = user.LoginInfo
}
But that's not very elegant. So, ultimately I re-wrote how I was handling unloaded information in componentDidMount() by creating a state prop called 'loading':
this.state = {
loading: true,
user: {}
};
In componentDidMount() I'm doing this:
this.setState({
user: JSON.parse(localStorage.getItem('user')),
loading: false
});
And in render():
const { loading, user } = this.state;
if (!loading) {
var { ConstituentId, UserId } = user.LoginInfo
}
console.log(ConstituentId)
It works great!
Basically, I'm just waiting for componentDidMount() to fire using the loading state by setting it to false in the function. Then we know it's loaded and can successfully render the data.

Ternary the operator inside the button and call the function

Is it possible to set the ternary operator and call the start() function. Under the influence of data from API, pending will change totrue.
I am trying to use the ternary operator inside the button and initiate the click event. Pending = true call the click event, thestart ()function.
{this.state.pending ? this.start() : null}
class Button extends Component {
constructor() {
super();
this.state = {
isRunning: false,
pending: false
};
}
componentDidMount() {
axios.get
axios({
url: "https://app/api/v1/running",
method: "GET",
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => {
console.log(response);
this.setState({
pending: response.data.pending //true
});
})
.catch(error => {
console.log(error);
})
}
start = () => {
console.log('AAAA');
}
render () {
return (
<button {this.state.pending ? this.start() : null} onClick={this.start}>
Start
</button>
);
}
}
You could take one of two approaches here.
1. Run the function straight from the api response success callback
.then(response => {
console.log(response);
this.setState({
pending: response.data.pending //true
});
this.start(); //Here
})
If you want to update the state before calling that function in any case then simply call the function in the callback of setStates second parameter. setState also accepts a function as its parameter and executes that function after performing the update. This is important to remember because setState operates asynchronously. So, If you want to make sure before your call the value has to update for some reason you can do this way. More about it here: https://reactjs.org/docs/react-component.html#setstate
.then(response => {
console.log(response);
this.setState({
pending: response.data.pending //true
}, this.start);
})
2. Use a component lifecycle method to listen for state changes and run the function
componentDidUpdate = (prevProps, prevState) => {
if(prevState.pending === false && this.state.pending === true){
this.start()
}
}
Every time you have a state or prop change componentDidUpdate executes. Note that it does not update when the component first mounts. And from the look of your code since pending is local to this component only you don't need to capture the case when it mounts firsts. More reference on lifecycle methods: https://reactjs.org/docs/react-component.html
Hope that clears your confusion here. Cheers!

Why does my add like method return "cannot read property of undefined"?

I wrote a method to handle adding a "like" on click. It's supposed to toggle isLiked to the opposite of the previous state and increment likes count by 1.
I think the error might be due to conflicting states but I'm not sure how to verify this.
addLike = postId => {
this.setState({
posts: this.state.posts.map(post => {
if (post.id === postId) {
return {
...post,
likes: post.likes + 1,
isLiked: !post.isLiked
};
}
return {
post
};
})
});
};
I was expecting like to increment by 1 and isLiked to be set to true. However, on click I'm getting an error that it "cannot read property of undefined".
You can code defensively against potentially missing properties:
if (post && post.id === postId) { // is it possible your posts array has `undefined` or `null` entry in it?
return {
...post,
likes: post.likes + 1;
isLiked: !post.isLiked
};
...
And elsewhere, where trying to access a property on a null or undefined object could throw that exception. For example, is state.posts properly populate or at least initialized to an empty array []? If not, this.state.posts.map could also throw the same error.
Furthermore, use React DevTools extension to inspect your state and check the values.
If your setState should rely on the previous state, then better go with the syntax with updater function. Something like
this.setState(({posts, ...restOfTheState}) =>
({
posts: posts.map( // mapping logic here
...restOfTheState,
});
BTW, state.posts was initialized, right?

React setstate return undefined on componentDidMount()

setState user property inside removeListener return undefined when i console.log() it inside component but when i check state in react developer tool user object from firebase it is there with actual value I want
state = {
coinList: {},
fav: [],
currentFavourite: '',
prices: [],
authed: false,
user: null
};
componentDidMount = () => {
this.removeListener = firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({
authed: true,
user
});
} else {
this.setState({
authed: false
});
}
});
this.ref = base.syncState('fav', {
context: this,
state: 'fav',
asArray: true
});}
If your console.log statement is inside the removeListener, I'd suspect that state hasn't been updated by the time console.log is called.
setState is asynchronous, so it's been updated in the background whilst the next statements are being.
You can provide setState with a function or statement that is only called once setState is completed....
this.setState({ user }, this.someFunction())
or simply...
this.setState({ user }, console.log(this.state.user));

Double rendering in React with asynchronous call in componentDidMount causing error

I'm building a blog application that has an articles index page, and from there you can click on an article to see the article or edit the article.
If you're going from the index page to the edit page, it works just fine because I already have all the articles in state. But if I refresh after I've gone to the edit-article page, I no longer have all the articles in state.
This is a problem because I'm making an asynchronous recieveSingleArticle call in the componentDidMount of my edit-article page, then I setState so my form is prepopulated. There's a double render which causes an "Uncaught TypeError: Cannot read property 'title' of undefined" error, presumably during the first render before the article has been received into state.
class ArticleEdit extends React.Component {
constructor(props) {
super(props);
this.state = {title: "", body: "", imageFile: ""};
this.handleChange = this.handleChange.bind(this);
this.handlePublish = this.handlePublish.bind(this);
this.handleFile = this.handleFile.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
componentDidMount() {
const { article, requestSingleArticle } = this.props;
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
...
I tried wrapping my async calls inside of an "if (this.props.article)" but that didn't work. Is there a best way of dealing with this type of problem? Any advice greatly appreciated!
UPDATE:
Another solution that works is to have a componentDidUpdate in addition to componentDidMount. check in componentDidMount if this.props.article exists and if so, setState. And in componentDidUpdate, wrap the setState in the following conditional:
if (!prevProps.article && this.props.article)
Just check if the article is present in the props before calling async action
componentDidMount() {
const { article, requestSingleArticle } = this.props;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}
Since you are not getting any render from this method , it means that the props are not yet obtained in the life cycle method componnetDidMount. So instead you can use componentWillReceiveProps like this
componentWillReceiveProps(nextProp) {
// this line here will check the article props' status so that
// we will not use setState each time we get a prop
if (this.props.article === nextProp.article) return;
// rest is just the same code from above
const { article, requestSingleArticle } = nextProp;
if (!(article && requestSingleArticle)) return; // this line
requestSingleArticle(this.props.match.params.articleID)
.then(() => {
this.setState({
title: article.title,
body: article.body,
imageFile: article.imageFile
});
});
}

Categories

Resources