React Native: Invariant Violation: Maximum update depth exceeded error - javascript

I know this can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate but in my situation even if I call setState just one time after that I got this error. Here is my state and componentDidUpdate:
constructor(props) {
super(props);
this.state = { usernameAlreadyUsing: false };
}
componentDidUpdate() {
if(this.props.usernames.includes(this.props.username)) {
this.setState({ usernameAlreadyUsing: true });
}
}

Every time setState called componentDidUpdate recalls itself. So
this situation causes you to enter an endless loop.
Here try this,
constructor(props) {
super(props);
this.state = { usernameAlreadyUsing: false };
}
componentDidUpdate() {
const { usernameAlreadyUsing } = this.state;
if(this.props.usernames.includes(this.props.username) && !usernameAlreadyUsing) {
this.setState({ usernameAlreadyUsing: true });
}
}
With this implementation your code only enters one time in componentDidUpdate.
Hope it works.

constructor(props) {
super(props);
this.state = { usernameAlreadyUsing: false };
}
componentDidUpdate(prevProps) {
if(this.props.usernames.length != prevProps.usernames.length) {
if(this.props.usernames.includes(this.props.username)) {
this.setState({ usernameAlreadyUsing: true });
}
}
}
try. this, problem with your above is , once its setting username is already present, its again calling setState as the condition succeeds every time. try this
Hope it helps.feel free for doubts

Related

componentDidUpdate - Error: Maximum update depth exceeded

class Suggestions extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
console.log(event.target.value)
}
componentDidUpdate(props, state) {
if(props.dataSearchBox) {
this.setState({visible: true})
} else {
this.setState({visible: false})
}
}
render() {
return (
<div className={`g${this.state.visible === true ? '': ' h'}`}>
<ul>
</ul>
</div>
);
}
}
export default Suggestions;
I am trying to set the state based on the props change, however I get the following error inside componentDidUpdate:
There was an error! Error: 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.
What am I doing wring here?
Your problem is here:
componentDidUpdate(props, state) {
if(props.dataSearchBox) {
this.setState({visible: true})
} else {
this.setState({visible: false})
}
}
componentDidUpdate gets called, it sets the state, which triggers componentDidUpdate... round and round.
Do you need a separate state property to handle this? That's a bit of an anti-pattern - you have what you need already as a prop, just use its value to control the visibility directly:
render() {
return (
<div className={`g${this.props.dataSearchBox ? '': ' h'}`}>
<ul>
</ul>
</div>
);
}

Can't call setState on an unmounted component (Async still running)

I have an asynchronous function like so:
componentDidMount() {
someAsyncFunction().then((data) => {
this.setState({ something: data });
});
}
If go back to the previous screen, I will get the following error:
Warning: Can't call setState (or forceUpdate) on an unmounted component.
Is there something I can do to cancel this setState() if I go back to a previous screen while the async Is still running?
You can use this workaround:
componentDidMount() {
this._ismounted = true;
someAsyncFunction().then((data) => {
if (this._ismounted) {
this.setState({ something: data });
}
});
}
componentWillUnmount() {
this._ismounted = false;
}
This way the sesState will be called only if the component is mounted.
But this (as suggested in the comments) is an antiPattern and is to be used only when there is no another way to cancel the asyncFunction instead of waiting for it to be solved and then make the check.
First, you should not use isMounted() to wrap your code in an if-Statement.
(https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html)
In your case I think you have several choices: You could fire an action in your asynchronous function which sets the redux state instead of the components state.
Or if you really need it, you could set a flag in your componentDidMount and set it on false in componentWillUnmount.
constructor(props){
super(props);
this.state={
mounted: true
}
}
componentDidMount() {
this.setState({mounted: true});
someAsyncFunction().then((data) => {
if(this.state.mounted) this.setState({ something: data });
});
}
componentWillUnmount() {
this.setState({mounted: false});
}

React don't change state after first method trigger

In my React app i have components structure:
-AllElements
--SingleElement
--SingleElementDetails
I am passing method See to SingleElement component where I invoke seefunc to invoke see method from AllElements component. The problem i my state (name) in AllElements not change after first onClick trigger, it changes after secund click. Could you tell my why ?
class AllElements extends Component {
constructor(props) {
super(props);
this.state = {
myData: [],
viewingElement: {
name:""
}
}
this.see = this.see.bind(this);
console.log('Initial Sate',this.state.viewingElement);
}
see(name) {
this.setState({
viewingElement: {
name:name
}
});
console.log('State after SEE',this.state.viewingElement);
}
render() {
const { myData, viewingElement } = this.state;
return (
<div>
{myData.map(se => (
<SingleElement
key={se.id}
name={se.name}
see={this.see}
/>
))}
<SingleElementDetails viewingElement={viewingElement}/>
</div>
);
}
}
class SingleElement extends Component {
constructor(props) {
super(props);
}
seefunc(name) {
this.props.see(this.props.name);
console.log('Name in seefunc props',this.props.name);
}
render() {
return (
<div onClick={this.seefunc.bind(this)}>
DIV CONTENT
</div>
)
}
}
The problem you have here is that setState is asynchronous. It does work the first time but you do not see it in your console.log because the console.log happens before the state is updated.
To see the updated state use the second argument of setState which is a callback function (https://reactjs.org/docs/react-component.html#setstate):
this.setState({
viewingElement: {
name:name
}
}, () => {
console.log('State after SEE',this.state.viewingElement);
});
And in SingleElement use the componentWillReceiveProps(nextprops) (https://reactjs.org/docs/react-component.html#componentwillreceiveprops) method from react lifecycle to see the updated props:
seefunc(name) {
this.props.see(this.props.name);
}
componentWillReceiveProps(nextprops) {
console.log('Name in props',nextProps.name);
}
It does change. However setState is an aync process so you're only logging the previous state to the console. setState does provide a callback that allows you to run code after the async process has finished, so you can do:
this.setState({
viewingElement: {
name:name
}
}, () => console.log('State after SEE',this.state.viewingElement));
DEMO

React component loops updating (GraphQL)

Good day!
I keep getting
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.
which looks obvious, but i fail to see the loop in my component.
ComponentWillUpdate() shows that it calls lots of rerender with the same props and state in a short amount of time.
Thanks in advance.
src/TitleList.js
class TitleList extends Component {
constructor(props) {
super(props)
this.state = {'items': null}
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
render() {
if (this.props.allTitlesQuery && this.props.allTitlesQuery.loading){
return <div>Loading</div>
}
if (this.props.allTitlesQuery && this.props.allTitlesQuery.error) {
return <div>Error!</div>
}
const titlesToRender = this.props.allTitlesQuery.allTitles
this.setState({'items': titlesToRender})
return <SortableList
items={this.state.items}
onSortEnd={this.onSortEnd}
/>;
}
}
When you call this.setState it calls your renderagain. So if you call setState from render it goes to recursive loop.
You can try something as :-
class TitleList extends Component {
constructor(props) {
super(props)
this.state = {'items': null}
}
componentDidMount () {
this.updateState(props);
}
componentWillReceiveProps (nextProps) {
if (this.props.allTitlesQuery.allTitles !== nextProps.allTitlesQuery.allTitles) {
this.setState(nextProps);
}
}
updateState (props) {
this.setState({"items":props.allTitlesQuery.allTitles});
}
onSortEnd = ({oldIndex, newIndex}) => {
this.setState({
items: arrayMove(this.state.items, oldIndex, newIndex),
});
};
render() {
if (this.props.allTitlesQuery && this.props.allTitlesQuery.loading){
return <div>Loading</div>
}
if (this.props.allTitlesQuery && this.props.allTitlesQuery.error) {
return <div>Error!</div>
}
return <SortableList
items={this.state.items}
onSortEnd={this.onSortEnd}
/>;
}
}
Use componentDidMount method to render the data for first time and if data changes update using componentWillReceiveProps method
the loop is caused by this.setState({'items': titlesToRender}) in your render function
You shouldn't be calling setState inside of render, do it in another lifecycle method like componentDidMount or componentWillReceiveProps:
Render shouldn't modify state: https://reactjs.org/docs/react-component.html#render

setting a state to a function that returns another state in react

I have two states, one state is set to a number and the other one is set to a function that returns the first state. I receive the error:
Uncaught TypeError: Cannot read property 'count' of undefined
Here is the snippet:
constructor(props) {
super(props);
this.state = {
count: 5,
date: this.full()
};
};
full = () => {
return this.state.count;
};
and here is the codepen link:
http://codepen.io/abdolsa/pen/wJXPqb?editors=0010
I think it might be a problem with binding but I have not been able to fix it yet.
Thank you
You are doing wrong. Value assigned to this.state inside constructor is not availbale untill initial render. If you want to set state with other state value than do this inside componentWillMount or componentDidMount method.
if you do something like this:
full = () => {
return 5;
};//it will work.
So the solution is,
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 5,
date: ""
};
this.full = this.full.bind(this);
};
componentWillMount ()
{
this.setState({date:this.full()})
}
full = () => {
return (this.state.count);
};
render() {
console.log(this.state)
return (
<h2>{this.state.date}.</h2>
);
}
}
ReactDOM.render(<Clock />, document.getElementById('root'));

Categories