Непойманное нарушение инварианта: слишком много повторных рендеров. React ограничивает количество рендеров, чтобы предотвратить бесконечный цикл

Я пытаюсь добавить закуску, чтобы отображать сообщение всякий раз, когда пользователь входит в систему или нет. Закусочная.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);

Когда я запускаю приложение, я получаю эту ошибку:

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, чтобы изменить состояние закусочной. Должен ли я использовать класс и componentShouldUpdate, чтобы не отображать несколько раз?

Вы, вероятно, используете Webpack, и в этом случае я хотел бы обратить ваше внимание на опцию инструмент разработчика, которая позволяет вам точно определить местонахождение ошибки в вашем исходном коде, вместо того, чтобы найти ошибку в вашем связанном коде, как у вас сейчас

Nino Filiu 20.03.2019 17:25

Можете ли вы проверить, вызывается ли handleClose несколько раз и решит ли проблема изменение handleClose = {handleClose} на handleClose = {()=>handleClose}?

Nicholas 20.03.2019 17:27

@Nicholas Я пробовал, но получил ту же ошибку

Slim 20.03.2019 17:50

у меня также была похожая проблема, но в моем случае это было связано с устаревшими значениями из предыдущих рендеров, я передал пустой массив зависимостей в useCallback и без необходимости обновлял состояние.

WasitShafi 04.08.2021 21:53

У меня была похожая ошибка, но это было из-за API, вызов которого дал сбой, и API автоматически удалился.

Yug Singh 17.01.2022 17:19
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
126
5
271 717
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

В SnackbarContentWrapper вам нужно изменить

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

к

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

Так что он запускает действие только при нажатии.

Кроме того, вы можете просто вставить handleClose в SignInContainer, чтобы

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

Это то же самое.

Спасибо, работает для меня. Это то, что я искал onClick = {() => onClose} .

jeetdeveloper 18.09.2019 08:45

Ага, ? спасибо; обновить мой вызов события с onClick = {onClose} на onClick = {() => onClose} Работал нормально.

José Jesús Ochoa Torres 11.08.2020 06:57

спасибо на самом деле даже я забыл это, теперь я вспомнил.

Rohan Devaki 08.10.2020 13:45

может кто-нибудь сказать мне, что нужно гуглить, чтобы понять разницу между onClick = {() => onClose} и onClick = {onClose}? Спасибо!

picklepick 13.11.2020 18:53

Погуглив что-нибудь на тему «связывание встроенных функций в реакции», вы должны получить то, что вам нужно. вам придется узнать об этом в js и функциях каррирования, это довольно кроличья нора.

Josh Pittman 15.12.2020 09:33

Именно то, что сработало для меня. Простой, но эффективный ответ. Весьма признателен.

Gishas 07.08.2021 15:34

Спасибо, это именно то, что я искал.

Bino Kochumol Varghese 01.11.2021 08:07

расскажите нам больше о технике карри

Snowmanzzz 17.11.2021 13:18

разница между onClick = {() => onClose} и onClick = {onClose}?

Snowmanzzz 18.11.2021 02:51

Я только учусь реагировать, и это сработало для меня!

Raju Ahmed 11.12.2021 19:36
Ответ принят как подходящий

Я подозреваю, что проблема заключается в том, что вы вызываете свой установщик состояния непосредственно внутри тела компонента функции, что заставляет React повторно вызывать вашу функцию снова с теми же реквизитами, что заканчивается повторным вызовом установщика состояния, который запускает Реагируйте, чтобы снова вызвать вашу функцию.... и так далее.

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

Вместо этого я рекомендую вам просто условно установить значение по умолчанию для свойства состояния, используя тернарный код, чтобы в итоге вы получили:

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 для полной демонстрации того, как он работает, а также сломанный компонент, который у вас был, и вы можете переключаться между ними.

ваш ответ решает проблему, описанную в вопросе, однако переменная open всегда false

Slim 20.03.2019 18:08

@ para008 Я добавил исчерпывающий демо, чтобы доказать, что он работает должным образом.

GregL 20.03.2019 18:45

Я не нахожу свою ошибку, потому что openвсегда ложно для меня, однако !!вариант сначала ложен, а затем становится истинным

Slim 20.03.2019 19:37

может быть, я должен задать это в другом вопросе. Спасибо за ответ.

Slim 20.03.2019 20:14

@ para008 Посмотрите обновленную демонстрацию. Теперь он должен полностью работать и имитировать, когда variant не установлен.

GregL 20.03.2019 20:20

Ваш ответ решил мою проблему, спасибо!

Patrick Martinus 02.01.2022 08:54

Вы должны связать событие в вашем onClick. Кроме того, функция click должна получать событие. См. пример

export default function Component(props) {

    function clickEvent (event, variable){
        console.info(variable);
    }

    return (
        <div>
            <IconButton
                key = "close"
                aria-label = "Close"
                color = "inherit"
                onClick = {e => clickEvent(e, 10)}
            >
        </div>
    )
}

У меня тоже такая же проблема, и решение в том, что я не привязывал событие в своем onClick. поэтому, когда он отображается в первый раз и данных больше, что заканчивается повторным вызовом установщика состояния, что запускает React для повторного вызова вашей функции и так далее.

export default function Component(props) {

function clickEvent (event, variable){
    console.info(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>
)
}

Вам нужно добавить событие, прежде чем вызывать функцию handleFunction следующим образом:

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

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

Вы можете предотвратить эту ошибку, используя хуки внутри функции.

В моем случае причина в неправильном attribute имени onblur = {setFieldTouched('firstName')} -->onBlur = {()=>setFieldTouched('firstName')} . после исправления ошибки имени атрибута исчезла

Я думаю, вы можете это сделать. У меня дома работает без проблем.

const handleClose = (reason) => {
  if (reason === 'clickaway') {
    return;
  }
  setSnackBarState(false);
};
<SnackBar
    open = {open}
    handleClose = {()=>handleClose(r)}
    variant = {variant}
    message = {message}
    />

Из того, что я собрал, Похоже, ошибка возникает, когда мы вызываем так много функций.

mostly setState()

Один из способов обойти это — установить состояния внутри функции, а не вызывать setState напрямую с помощью onClick, например.

Использование большого количества таких состояний, вероятно, приводит к повторному рендерингу реакции несколько раз, вызывая эту ошибку.

Другие вопросы по теме