Open Modal on page load causing Maximum update depth exceeded - reactjs

I'm trying to open a modal on page load but I get 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.
I know why this is happening because there are many posts about it. I don't know how to fix it though.
I'm using the Material UI modal
I've tried loading it in componentDidMount- caused error. I tried also to use onClick and simulate a click to open it, but this did not work - could not get simulation to work. Below is my latest attempt - caused error.
<Button onLoad={this.openModal.bind(this)()}>Open Modal</Button>
openModal(e) {
this.setState({
open:true
})
}
I can't seem to get past the error and open the modal on load.
In the Modal file, the modal itself looks like this:
<Button onLoad={this.openModal.bind(this)()}>Open Modal</Button>
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.state.open}
onClose={this.handleClose}
>
In the parent component
<Modal open={this.state.modalState}/> //=> true
I tried this as well (with the event handler removed and not being called this.openModal.bind(this)()). Got the error
componentDidUpdate(){
this.setState({
open:this.props.open
})
}

Generally, for rendering a modal we will use a state to specify is its open or close, If you want to open the modal as soon as the component loads then you can specify the state open as true by default
for example
Class Example extends React.component {
construtor(props) {
super(props)
this.state = { open: true }
}
render() {
return <Modal open={this.state.open}>{//body goes here}</Modal>
}
}
This opens modal by default and you can toggle it depending on your requirement
You are calling setstate in componentdidupdate with out any conditions this will cause stacklevel too deep error as after setstate the componentwillupdate will be invoked again
If you want you can use componentdidmount instead

Related

React useState reruns the component function on no change

in this very simple demo
import { useState } from 'react';
function App() {
const [check, setCheck] = useState(false);
console.log('App component Init');
return (
<div>
<h2>Let's get started! </h2>
<button
onClick={() => {
setCheck(true);
}}
>
ClickMe
</button>
</div>
);
}
export default App;
i get one log on app init,
upon the first click (state changes from false to true) i get another log as expected.
But on the second click i also get a log , although the state remains the same.(interstingly the ReactDevTools doesn't produce the highlight effect around the component as when it is rerendered)
For every following clicks no log is displayed.
Why is this extra log happening.
Here is a stackblitz demo:
https://stackblitz.com/edit/react-ts-wvaymj?file=index.tsx
Thanks in advance
Given
i get one log on app init,
upon the first click (state changes from false to true) i get another
log as expected.
But on the second click i also get a log , although the state remains
the same.(interstingly the ReactDevTools doesn't produce the highlight
effect around the component as when it is rerendered)
For every following clicks no log is displayed.
And your question being
Why is this extra log happening?
Check the useState Bailing Out of a State Update section (emphasis mine):
If you update a State Hook to the same value as the current state,
React will bail out without rendering the children or firing effects.
(React uses the Object.is comparison algorithm.)
Note that React may still need to render that specific component again
before bailing out. That shouldn’t be a concern because React won’t
unnecessarily go “deeper” into the tree. If you’re doing expensive
calculations while rendering, you can optimize them with useMemo.
The answer to your question is essentially, "It's just the way React and the useState hook work." I'm guessing the second additional render is to check that no children components need to be updated, and once confirmed, all further state updates of the same value are ignored.
If you console log check you can see...
The first click you will get check is true -> check state change from false (init) => true (click) => state change => view change => log expected.
The second click => check state is true (from frist click) => true => state not change => view not render.
So. you can try
setCheck(!check);

React onClick event trigger infinitely on render

So i ran into some funny bug with React onClick event. So if I write something like this:
login = () => {
this.setState({ authed: true })
alert(this.state.authed)
}
render() {
return (
<div>
<Loginpage />
<button onClick={this.login}>test</button>
</div>
);
it would function normally but if onClick is changed to onClick={this.login()}, this event will be triggered on render and continue infinitely even after changing the code back and re-render this will still go on. I'm just curious why it ends up like that, does anyone knows?
If you use this onClick={this.login()} you are calling the function as soon as it renders and not onClick, that's why your component is infinitelly rendering.
So if you do this:
<button onClick={this.login}>test</button>
You are passing a reference to the function login on click of the button.
But if you do this:
<button onClick={this.login()}>test</button>
Then you are executing the function as soon as this element is rendered.
The login function triggers a state change:
login = () => {
this.setState({ authed: true })
alert(this.state.authed)
}
And since state change triggers another render, therefore it goes into the infinite loop.
Had you not called the setState in login, it would not go into the infinite loop.
Only your function will execute without even clicking the button on render.
Try this
<button onClick={()=>this.login}>test</button>

How to open a form in new tab/window in reactjs

i wan to open a form in new window. i am following David Gilbertson
post
to open new window.
i wrote my form here
{this.state.showWindowPortal && (
<MyWindowPortal>
<div>
<input type="file" onChange={this.handleChange} />
<img src={this.state.file}/>
</div>
</MyWindowPortal>
)}
and i wrote this handle change in my main component. but i am able to make a call to this function.
i have two component
class DisplayCard extends React.Component {}
and
class MyWindowPortal extends React.PureComponent {}
i also tried to write this function in MyWindowPortal but still unable to call this function
handleChange=(event)=> {
console.log("in image",event)
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
}
here is the sandbox url for the same.
https://codesandbox.io/s/sad-driscoll-wetlw
Dont know why but adding the following lines to App.js makes it work
componentDidMount() {
window.setInterval(() => this.setState({}), 500);
}
May be The clicks on portal is updating the react state but its not updating the DOM, So, due to that setInterval, it triggers rerender and during that re render its checking the state and unmonting the component.
I cant tell you the exact workaround or why this is happening, will look in to it

React-to-print: how to change state in onAfterPrint when closing print window

I am using react-to-print library, and using that, I want to change a state when closing print window.
When I click cancel button, it works fine. However, if I click X (exit) button, it will crash.
I have the following code.
<ReactToPrint
trigger={() => (
...
)}
content={() => ...}
onBeforePrint={() => { this.setState({ printState: true}) }}
onAfterPrint={() => { this.setState({ printState: false }) }}
closeAfterPrint
/>
Here's the error message
Uncaught Invariant Violation: 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.
It might not be it, but try using onAfterPrint={() => this.handlePrintAfter()}
This is a very edge case where your component is re-rendered after the dialog opened, so you have to use flushSync as outlined in the React-DOM docs here to force a full re-render before the dialog opens:
https://beta.reactjs.org/reference/react-dom/flushSync#usage
Hope that helps.

State update on unmounting component - how to fix that?

I have some problems with updating state on an unmounted component as below:
All works as expected. Modal is closed after the comment is deleted. But I'm getting a warning:
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the
componentWillUnmount method
I know what causes the problem but I don't know how to fix that. Here's my data flow:
This function invokes an action responsible for deleting user posts:
async handleDelete() {
const { articleID, comment, deleteComment } = this.props;
await deleteComment({ articleID, idcomment: comment.idcomment });
//showModal flag is set to false - hide confirmation modal after clicking 'yes' button
this.setState({showModal: !this.state.showModal})
}
Action listed above 'deleteComment' looks like this:
export const deleteComment = data => async dispatch => {
const { idcomment, articleID } = data;
try {
await axios.post(`/api/comments/${idcomment}`, {_method: 'DELETE'});
//fetchling comments again to update a state and it looks like it causes the problem with updating a state on unmounted component because when I commented out that line, it didnt happen anymore.
await dispatch(fetchComments(articleID));
}
catch(e) {
throw new Error(e) }
finally {
dispatch(setCommentStatus(true));
dispatch(decCommentCount());
}
}
Not my question is, how to fix that? I want to close my confirmation modal after the comment is deleted from the database and the new sets of comments are already updated.
Here is how I use my modal:
<Modal
showModal={showModal}
accept={this.handleDelete}
denied={() => this.setState({showModal: !this.state.showModal})}
/>
And the last one is the modal itself:
return (
!showModal
? ''
: (
<Wrapper>
<ModalSection>
<Header>
<Title>Usunięcie komentarza</Title>
<ButtonExit onClick={denied}>❌</ButtonExit>
</Header>
<Text>Czy jesteś pewien, że chcesz usunąć ten komentarz? <br /> Nie będziesz mógł cofnąć tej operacji.</Text>
<Footer>
<div>
<Button onClick={denied}>Cofnij</Button>
<Button warning onClick={accept}>Usuń</Button>
</div>
</Footer>
</ModalSection>
</Wrapper>
)
)
Apparently it works after deleting this.setState({showModal: !this.state.showModal}) from handleDelete function. Weird thing is after finishing deleting comment my modal is closing and I don't know HOW.. Something changing showModal value to false... Still figuring out.
EDIT: IM DUMB. After deleting comment I'm fetching comment list again AND that component has state of showModal set to FALSE by default so when component is rerendering the state is default which is false.................. SORRY.
It is because you are trying to modify state of Modal component after its being closed. Instead you should maintain state of comment deletion in the Parent component which renders the Modal component instead. And update the state in Parent component using a callback. This callback can be passed to Modal component as a prop and called when a delete action occurs. This should fix the issue.
You shouldn't use tenary to render the modal (!showModal ? '' : <...>) You should pass a prop to your modal that only hides or shows the modal.
If you see some libraries they have a show prop or isOpen
e.g. react-bootstrap react-modal
They have that prop so you don't have errors like the one you are having.

Resources