未捕获的不变违规:重新渲染过多 React限制渲染次数以防止无限循环
我怀疑问题在于您正在函数组件主体内部立即调用状态设置器,这迫使React使用相同的道具再次重新调用您的函数,最终导致再次调用状态设置器,从而触发做出反应以再次调用您的函数……等等。
@H_502_2@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> ) }
相反,我建议您仅使用三元有条件地设置state属性的默认值,因此最终得到:
@H_502_2@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> ) }
综合演示
请参阅此CodeSandbox.io演示以全面了解其工作原理,以及损坏的组件,您可以在两者之间进行切换。
解决方法
我试图添加一个快餐栏,以便在用户登录或不登录时显示一条消息。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,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);
当我运行应用程序时,出现以下错误:
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)
问题是由于SnackBar组件。我使用useState
钩子来更改SnackBar的状态。我是否应该使用class和acomponentShouldUpdate
以便不多次渲染?