Nicht erfasste unveränderliche Verletzung: Zu viele Re-Renderings. React begrenzt die Anzahl der Renderings, um eine Endlosschleife zu verhindern

Lesezeit: 7 Minuten

Benutzer-Avatar
Schlank

Ich versuche, eine SnackBar hinzuzufügen, um eine Nachricht anzuzeigen, wenn sich ein Benutzer anmeldet oder nicht. 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);

Wenn ich die Anwendung ausführe, erhalte ich diesen Fehler:

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)

Das Problem liegt an der SnackBar-Komponente. Ich verwende das useStateHooks, um den Zustand der snackBar zu ändern. Soll ich eine Klasse und a verwenden componentShouldUpdate um nicht mehrfach zu rendern?

  • Sie verwenden wahrscheinlich Webpack, in diesem Fall möchte ich Sie auf das aufmerksam machen Entwicklungstool Option, mit der Sie den Fehler genau in Ihrem Originalcode lokalisieren können, anstatt den Fehler wie bisher in Ihrem gebündelten Code zu lokalisieren

    – Nino Filius

    20. März 2019 um 16:25 Uhr

  • Können Sie überprüfen, ob handleClose mehrfach aufgerufen wird und wenn durch Wechseln handleClose={handleClose} zu handleClose={()=>handleClose} wird das Problem lösen?

    – Nikolaus

    20. März 2019 um 16:27 Uhr

  • @Nicholas Ich habe es versucht, aber ich habe den gleichen Fehler bekommen

    – Schlank

    20. März 2019 um 16:50 Uhr

  • Ich hatte auch ein ähnliches Problem, aber in meinem Fall lag es an veralteten Werten von früheren Renderings, ich hatte ein leeres Abhängigkeitsarray in useCallback übergeben und den Status unnötigerweise aktualisiert.

    – WasitShafi

    4. August 2021 um 19:53 Uhr

  • Ich hatte einen ähnlichen Fehler, aber das lag an einer API, deren Aufruf fehlschlug, und es gab automatische Rückzüge auf der API.

    – Yug Singh

    17. Januar um 16:19 Uhr

Benutzer-Avatar
GregL

Ich vermute, dass das Problem in der Tatsache liegt, dass Sie Ihren Zustandssetzer unmittelbar innerhalb des Funktionskomponentenkörpers aufrufen, was React dazu zwingt, Ihre Funktion erneut aufzurufen, mit denselben Requisiten, was dazu führt, dass der Zustandssetzer erneut aufgerufen wird, was auslöst Reagieren Sie, um Ihre Funktion erneut aufzurufen…. und so weiter.

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>
    )
}

Stattdessen empfehle ich Ihnen, den Standardwert für die State-Eigenschaft einfach bedingt mit einem Ternärwert festzulegen, sodass Sie am Ende Folgendes erhalten:

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>
    )
}

Umfassende Demo

Sieh dir das an CodeSandbox.io-Demo für eine umfassende Demo davon, wie es funktioniert, plus die defekte Komponente, die Sie hatten, und Sie können zwischen den beiden umschalten.

  • Ihre Antwort löst das in der Frage beschriebene Problem, jedoch die Variable open stets false

    – Schlank

    20. März 2019 um 17:08 Uhr

  • @ para008 Ich habe eine umfassende hinzugefügt Demo um zu beweisen, dass es so funktioniert, wie es sollte.

    – GregL

    20. März 2019 um 17:45 Uhr

  • Ich finde meinen Fehler nicht weil openbei mir aber immer falsch !!variante ist am anfang falsch dann wird es wahr

    – Schlank

    20. März 2019 um 18:37 Uhr

  • vielleicht sollte ich es in einer anderen Frage stellen. Danke für deine Antwort.

    – Schlank

    20. März 2019 um 19:14 Uhr

  • Ihre Antwort löst mein Problem, danke!

    – Patrick Martinus

    2. Januar um 7:54

Benutzer-Avatar
Josh Pittmann

Im SnackbarContentWrapper du musst dich ändern

<IconButton
  key="close"
  aria-label="Close"
  color="inherit"
  className={classes.close}
  onClick={onClose} // change this
>

zu

<IconButton
  key="close"
  aria-label="Close"
  color="inherit"
  className={classes.close}
  onClick={() => onClose()} // to this
>

Damit die Aktion nur ausgelöst wird, wenn Sie klicken.

Alternativ könntest du auch einfach Curry machen handleClose in SignInContainer zu

const handleClose = () => (reason) => {
  if (reason === 'clickaway') {
    return;
  }
  setSnackBarState(false)
};

Es ist das gleiche.

  • Danke, funktioniert bei mir. Das ist, wonach ich gesucht habe onClick={() => onClose} .

    – jetdeveloper

    18. September 2019 um 6:45 Uhr


  • kann mir jemand sagen, was ich googeln soll, um den Unterschied zwischen onClick={() => onClose} und onClick={onClose} zu verstehen? Vielen Dank!

    – Essiggurke

    13. November 2020 um 17:53 Uhr

  • Wenn Sie etwas nach dem Effekt “Inline-Funktionen in Reaktion binden” googeln, sollten Sie dorthin gelangen, wo Sie hin müssen. Sie müssen etwas über ‘this’ in JS und Currying-Funktionen lernen, es ist ein ziemlicher Kaninchenbau.

    – Josh Pittmann

    15. Dezember 2020 um 8:33 Uhr

  • Genau das, was für mich funktioniert hat. Einfache, aber effektive Antwort. Herzlichen Dank.

    – Gishas

    7. August 2021 um 13:34 Uhr

  • Unterschied zwischen onClick={() => onClose} und onClick={onClose}?

    – Schneemannzzz

    18. November 2021 um 1:51 Uhr

Sie müssen ein Ereignis in Ihrem onClick verknüpfen. Außerdem muss die Klickfunktion das Ereignis empfangen. Siehe Beispiel

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>
    )
}

Benutzer-Avatar
Cristian Marin

Sie müssen ein Ereignis hinzufügen, bevor Sie Ihre handleFunction wie folgt aufrufen:

function SingInContainer() {
..
..
handleClose = () => {
}

return (
    <SnackBar
        open={open}
        handleClose={() => handleClose}
        variant={variant}
        message={message}
        />
    <SignInForm/>
  )
}

Sie können diesen Fehler verhindern, indem Sie innerhalb einer Funktion Hooks verwenden

Benutzer-Avatar
kushagra-aa

Nach dem, was ich gesammelt habe, scheint der Fehler verursacht zu werden, wenn wir so viele Funktionen aufrufen.

meist setState()

Eine Möglichkeit, dies zu umgehen, besteht darin, die Zustände in einer Funktion festzulegen, anstatt die aufzurufen setState direkt vorbei onClick zum Beispiel.

Die Verwendung vieler solcher Zustände führt wahrscheinlich dazu, dass die Reaktion mehrmals neu gerendert wird, was diesen Fehler verursacht.

Ich habe auch das gleiche Problem, und die Lösung ist, dass ich das Ereignis nicht in meinem onClick gebunden habe. Wenn es also zum ersten Mal gerendert wird und mehr Daten vorhanden sind, wird der Zustandssetzer erneut aufgerufen, was React dazu veranlasst, Ihre Funktion erneut aufzurufen, und so weiter.

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>
)
}

1015310cookie-checkNicht erfasste unveränderliche Verletzung: Zu viele Re-Renderings. React begrenzt die Anzahl der Renderings, um eine Endlosschleife zu verhindern

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy