Uncaught Invariant Violation: отрендерено больше хуков, чем во время предыдущего рендеринга

У меня есть компонент, который выглядит так (очень упрощенная версия):

const component = (props: PropTypes) => {

    const [allResultsVisible, setAllResultsVisible] = useState(false);

    const renderResults = () => {
        return (
            <section>
                <p onClick = { setAllResultsVisible(!allResultsVisible) }>
                    More results v
                </p>
                {
                    allResultsVisible &&
                        <section className = "entity-block--hidden-results">
                            ...
                        </section>
                }
            </section>
        );
    };

    return <div>{ renderResults() }</div>;
}

Когда я загружаю страницу, на которой используется этот компонент, я получаю эту ошибку: Uncaught Invariant Violation: Rendered more hooks than during the previous render. Я пытался найти объяснение этой ошибки, но поиск не дал результатов.

Когда я немного изменяю компонент:

const component = (props: PropTypes) => {

    const [allResultsVisible, setAllResultsVisible] = useState(false);

    const handleToggle = () => {
        setAllResultsVisible(!allResultsVisible);
    }

    const renderResults = () => {
        return (
            <section>
                <p onClick = { handleToggle }>
                    More results v
                </p>
                {
                    allResultsVisible &&
                        <section className = "entity-block--hidden-results">
                            ...
                        </section>
                }
            </section>
        );
    };

    return <div>{ renderResults() }</div>;
}

Я больше не получаю эту ошибку. Это потому, что я включил функцию setState в jsx, который возвращает renderResults? Было бы здорово иметь объяснение, почему исправление работает.

Поведение ключевого слова "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) для оценки ваших знаний,...
81
0
123 013
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Исправление работает, потому что первый образец кода (с ошибкой) вызывает функцию внутри onClick, а второй (рабочий) передает функцию onClick. Разница заключается в тех самых важных скобках, которые в JavaScript означают «вызвать этот код».

Подумайте об этом так: в первом примере кода каждый раз, когда рендерится component, вызывается renderResults. Каждый раз, когда это происходит, вызывается setAllResultsVisible(!allResultsVisible), а не ожидание щелчка. Поскольку React выполняет рендеринг по собственному расписанию, неизвестно, сколько раз это произойдет.

Из документов React:

With JSX you pass a function as the event handler, rather than a string.

Документы по обработке событий React

Примечание. Мне не удалось получить это точное сообщение об ошибке при запуске первого примера кода в песочнице. Моя ошибка относится к бесконечному циклу. Может быть, более свежая версия React выдает описанную ошибку?

Правильно, большая ошибка с моей стороны. Я постоянно путаю () => function() и function() в onClicks.

timothym 11.04.2019 02:08

Вы можете просто изменить добавление события onclick () => перед setAllResultsVisible.

<p onClick = {() => setAllResultsVisible(!allResultsVisible) }> 
    More results v
</p>

и он будет работать отлично

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

function Comp(props){return <div>{props.val}</div>}

Этот компонент можно вызвать в jsx следующими способами:

1. <Comp val = {3} /> // works well
2. { Comp({val:3}) } // throws uncaught invariant violation error, at least it throw in my case, may be there were other factors, but changing back to first way removed that problem

Посмотреть вопрос можно React :

  1. Рендерится меньше хуков, чем в предыдущем рендере.
  2. Отрендерено больше хуков, чем в предыдущем рендере.

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

const parentFunc = () => {
    if (case==1)
        return function_a();
    if (case==2)
        return function_b();
}

теперь function_a() может быть функцией, создающей два или один хук, предположим, useStyle() или что-то еще

а function_b() может быть функцией, не создающей ловушку.

Теперь, когда parentFunc возвращает function_a(), отображающую один хук, а function_b(), не рендерящий хук, тогда реакция сообщит вам, что из одной и той же функции рендеринга были возвращены два разных рендера, один с двумя или одним хуком, а другой с одним хуком, это несоответствие приводит к Ошибка. Ошибка

less hooks were rendered. And the error is quite obvious.

Когда случаи меняются местами и функция_b() возвращается первой причиной условных выражений, тогда реакция сообщит вам, что из одной и той же функции рендеринга были возвращены разные рендеры, и будет ошибка.

Rendered more hooks than previous render.

Теперь решение:

Измените поток кода, например, создайте функцию function_ab(), которая обеспечит визуализацию всех используемых хуков и в этой функции:

const function_ab = () => {
    if (case==1)
         return (<div></div>) //or whatever
    if (case==2)
         return (<div>I am 2 </div>) //or whatever
}

Проблема заключается в onClick, поскольку setAllResultsVisible вызывается, это вызывает изменение состояния и результат при каждом рендеринге.

onClick = { setAllResultsVisible(!allResultsVisible) }

Вместо этого измените это на вызов функции:

onClick = {_ => setAllResultsVisible(!allResultsVisible) }

Я столкнулся с той же проблемой. Я делал что-то вроде этого:

const Table = (listings) => {

    const {isLoading} = useSelector(state => state.tableReducer);

    if (isLoading){
        return <h1>Loading...</h1>
    }

    useEffect(() => {
       console.info("Run something")
    }, [])

    return (<table>{listings}</table>)
}

Я думаю, что произошло то, что при первом рендеринге компонент вернулся раньше, а useEffect не запустился. Когда состояние isLoading изменилось, запустился useEffect, и я получил ошибку — хук отрендерился больше раз, чем предыдущий рендер.

Простое изменение исправило это:

const Table = (listings) => {
    
    const {isLoading} = useSelector(state => state.tableReducer);
        
    useEffect(() => {
        console.info("Run something")
    }, [])
    
    if (isLoading){
        return <h1>Loading...</h1>
    }
    return (<table>{listings}</table>)
}

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