'Uncaught Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop
I'm trying to add a snackBar in order to display a message whenever a user signIn or not. SnackBar.jsx:
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error";
import CloseIcon from "@material-ui/icons/Close";
import green from "@material-ui/core/colors/green";
import IconButton from "@material-ui/core/IconButton";
import Snackbar from "@material-ui/core/Snackbar";
import SnackbarContent from "@material-ui/core/SnackbarContent";
import { withStyles } from "@material-ui/core/styles";
const variantIcon = {
success: CheckCircleIcon,
error: ErrorIcon
};
const styles1 = theme => ({
success: {
backgroundColor: green[600]
},
error: {
backgroundColor: theme.palette.error.dark
},
icon: {
fontSize: 20
},
iconVariant: {
opacity: 0.9,
marginRight: theme.spacing.unit
},
message: {
display: "flex",
alignItems: "center"
}
});
function SnackbarContentWrapper(props) {
const { classes, className, message, onClose, variant, ...other } = props;
const Icon = variantIcon[variant];
return (
<SnackbarContent
className={classNames(classes[variant], className)}
aria-describedby="client-snackbar"
message={(
<span className={classes.message}>
<Icon className={classNames(classes.icon, classes.iconVariant)} />
{message}
</span>
)}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose}
>
<CloseIcon className={classes.icon} />
</IconButton>
]}
{...other}
/>
);
}
SnackbarContentWrapper.propTypes = {
classes: PropTypes.shape({
success: PropTypes.string,
error: PropTypes.string,
icon: PropTypes.string,
iconVariant: PropTypes.string,
message: PropTypes.string,
}).isRequired,
className: PropTypes.string.isRequired,
message: PropTypes.node.isRequired,
onClose: PropTypes.func.isRequired,
variant: PropTypes.oneOf(["success", "error"]).isRequired
};
const MySnackbarContentWrapper = withStyles(styles1)(SnackbarContentWrapper);
const CustomizedSnackbar = ({
open,
handleClose,
variant,
message
}) => {
return (
<div>
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
open={open}
autoHideDuration={6000}
onClose={handleClose}
>
<MySnackbarContentWrapper
onClose={handleClose}
variant={variant}
message={message}
/>
</Snackbar>
</div>
);
};
CustomizedSnackbar.propTypes = {
open: PropTypes.bool.isRequired,
handleClose: PropTypes.func.isRequired,
variant: PropTypes.string.isRequired,
message: PropTypes.string.isRequired
};
export default CustomizedSnackbar;
SignInFormContainer.jsx:
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import SnackBar from '../../components/SnackBar';
import SignInForm from './SignInForm';
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(false);
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
if (variant) {
setSnackBarState(true);
}
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
SingInContainer.propTypes = {
variant: PropTypes.string.isRequired,
message: PropTypes.string.isRequired
}
const mapStateToProps = (state) => {
const {variant, message } = state.snackBar;
return {
variant,
message
}
}
export default connect(mapStateToProps)(SingInContainer);
When I run the application I got this error:
Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at invariant (http://localhost:9000/bundle.js:34484:15)
at dispatchAction (http://localhost:9000/bundle.js:47879:44)
at SingInContainer (http://localhost:9000/bundle.js:79135:5)
at renderWithHooks (http://localhost:9000/bundle.js:47343:18)
at updateFunctionComponent (http://localhost:9000/bundle.js:49010:20)
at beginWork (http://localhost:9000/bundle.js:50020:16)
at performUnitOfWork (http://localhost:9000/bundle.js:53695:12)
at workLoop (http://localhost:9000/bundle.js:53735:24)
at HTMLUnknownElement.callCallback (http://localhost:9000/bundle.js:34578:14)
at Object.invokeGuardedCallbackDev (http://localhost:9000/bundle.js:34628:16)
The problem is due to the SnackBar component. I use the useState
hooks in order to change the state of the snackBar. Should I use a class and a componentShouldUpdate
in order to not render multiple times?
Solution 1:[1]
I suspect that the problem lies in the fact that you are calling your state setter immediately inside the function component body, which forces React to re-invoke your function again, with the same props, which ends up calling the state setter again, which triggers React to call your function again.... and so on.
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(false);
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
if (variant) {
setSnackBarState(true); // HERE BE DRAGONS
}
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
Instead, I recommend you just conditionally set the default value for the state property using a ternary, so you end up with:
const SingInContainer = ({ message, variant}) => {
const [open, setSnackBarState] = useState(variant ? true : false);
// or useState(!!variant);
// or useState(Boolean(variant));
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
return (
<div>
<SnackBar
open={open}
handleClose={handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
</div>
)
}
Comprehensive Demo
See this CodeSandbox.io demo for a comprehensive demo of it working, plus the broken component you had, and you can toggle between the two.
Solution 2:[2]
In SnackbarContentWrapper
you need to change
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={onClose} // change this
>
to
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() => onClose()} // to this
>
So that it only fires the action when you click.
Alternatively, you could just curry the handleClose
in SignInContainer
to
const handleClose = () => (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false)
};
It's the same.
Solution 3:[3]
You must link an event in your onClick. Additionally, the click function must receive the event. See the example
export default function Component(props) {
function clickEvent (event, variable){
console.log(variable);
}
return (
<div>
<IconButton
key="close"
aria-label="Close"
color="inherit"
onClick={e => clickEvent(e, 10)}
>
</div>
)
}
Solution 4:[4]
You need to add an event, before call your handleFunction like this:
function SingInContainer() {
..
..
handleClose = () => {
}
return (
<SnackBar
open={open}
handleClose={() => handleClose}
variant={variant}
message={message}
/>
<SignInForm/>
)
}
Solution 5:[5]
You can prevent from this error by using hooks inside a function
Solution 6:[6]
From what I've gathered, It seems like the error is caused when we call so many functions.
mostly
setState()
One way around this will be to set the states inside a function instead of calling the setState
directly by onClick
for example.
Using a lot of states like this probably causes react to re-render multiple times, causing this error.
Solution 7:[7]
I also have the same problem, and the solution is I didn't bind the event in my onClick. so when it renders for the first time and the data is more, which ends up calling the state setter again, which triggers React to call your function again and so on.
export default function Component(props) {
function clickEvent (event, variable){
console.log(variable);
}
return (
<div>
<IconButton
key="close"
aria-label="Close"
color="inherit"
onClick={e => clickEvent(e, 10)} // or you can call like this:onClick={() => clickEvent(10)}
>
</div>
)
}
Solution 8:[8]
In my case its cause because of wrong attribute
name onblur={setFieldTouched('firstName')} -->onBlur={()=>setFieldTouched('firstName')}
. after correcting attribute name error gone
Solution 9:[9]
I think you can do this. At my place it works without any problem.
const handleClose = (reason) => {
if (reason === 'clickaway') {
return;
}
setSnackBarState(false);
};
<SnackBar
open={open}
handleClose={()=>handleClose(r)}
variant={variant}
message={message}
/>
Solution 10:[10]
See That you have included preventDefault() or not. In my case, I forgot to use that and later I find that mistake.
Solution 11:[11]
While calling function add event like this
before
<button onClick={ decrementCount() }>-</button>
after
<button onClick={ () => decrementCount() }>-</button>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow