Перехватчик useState не работает с перехватчиком usePrevious

Я создал компонент меню и пытаюсь использовать ловушку useState для хранения открытых подменю. Когда меню закрыто (от родителя, использующего реквизиты), я хочу закрыть все подменю, и для этого я использую хук usePrevious из библиотеки react-hanger, чтобы определить, когда главное меню переходит из OPEN> CLOSED. Вот мой код.

import React, { useState } from 'react';
import { usePrevious } from "react-hanger"

const defaultMenusOpen = {menu1:false, menu2:false}

function Menu(props) {

    const [subMenusOpen, setSubMenusOpen] = useState(defaultMenusOpen))
    const prevIsOpen = usePrevious(props.isOpen);


    if(props.isOpen === false && prevIsOpen === true){
        setSubMenusOpen(defaultMenusOpen)
    }

    return (
        {props.isOpen && 
            ... JSX
        }
    );

}

export default Menu

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

Похоже, это связано с тем, что оператор if имеет значение TRUE при каждом повторном рендеринге, потому что вызов setSubMenusOpen, похоже, не заставляет usePrevious снова сохранять новое значение. Я думаю, вот что происходит.

  • props.is: открыть изменения с ИСТИНА> ЛОЖЬ
  • prevIsOpen и props.isOpen в этот момент имеют значения TRUE и FALSE, поэтому ...
  • setSubMenusOpen () вызывает повторный рендеринг.
  • Вместо previsOpen и props.isOpen, которые теперь имеют значение FALSE и FALSE, они остаются неизменными, поэтому setSubMenusOpen вызывается снова, ad finitum

Любая помощь будет принята с благодарностью.


person jonhobbs    schedule 11.12.2018    source источник


Ответы (1)


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

function Menu(props) {

    const [subMenusOpen, setSubMenusOpen] = useState(defaultMenusOpen))
    const prevIsOpen = usePrevious(props.isOpen);

    useEffect(() => {
        if(props.isOpen === false && prevIsOpen === true){
            setSubMenusOpen(defaultMenusOpen)
        }
    }, [props.isOpen])


    return (
        {props.isOpen && 
            ... JSX
        }
    );

}

export default Menu
person Shubham Khatri    schedule 12.12.2018
comment
Большое спасибо, я попробую это сегодня вечером. Самое сложное при переходе на функциональные компоненты / крючки - это понимание жизненного цикла. Как новичок, похоже, что код внутри useEffect будет запускаться только один раз при монтировании компонента, а не каждый раз при изменении свойств. - person jonhobbs; 12.12.2018
comment
Подождите секунду, если useEffect () является эквивалентом componentDidMount () и componentDidUnmount (), то что является эквивалентом componentDidUpdate ()? - person jonhobbs; 13.12.2018
comment
useEffect не является эквивалентом componentDidMount, его можно использовать для репликации поведения или componentDidMount, componentDidUpdate, componentWillUnmount - person Shubham Khatri; 13.12.2018
comment
Второй параметр useEffect определяет, когда этот эффект будет выполнен. Если второй параметр не определен, он выполняется при каждом рендеринге, если указан пустой массив, он выполняет обратный вызов после первоначального рендеринга, если значения являются значениями в массиве, тогда он проверяет, есть ли изменения в любом из этих значений, а затем решает, есть ли вызывать обратный вызов или нет - person Shubham Khatri; 13.12.2018
comment
Спасибо, так что в основном он запускается при монтировании и каждом последующем рендеринге, но вы можете указать ему повторно рендерить только при изменении предоставленных реквизитов. Единственное, чего, я думаю, не хватает, - это возможности решать, выполнять рендеринг или нет, основываясь на какой-то другой логике. - person jonhobbs; 13.12.2018