Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useState hook not working with usePrevious hook

I've created a menu component and I'm trying to use the useState hook to store which sub-menus are open. When the menu is closed (from the parent using props) I want to close all sub-menus and to do this I've using the usePrevious hook from the react-hanger library to determine when the main menu is going from OPEN > CLOSED . Here is my code.

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

The problem is that this is causing an infinite loop error and continually re-rendering the Menu.

This seems to be because he if statement if TRUE on every re-render because calling setSubMenusOpen doesn't seem to cause usePrevious to store the new value again. This is what I think is happening.

  • props.isOpen changes from TRUE > FALSE
  • prevIsOpen and props.isOpen are TRUE and FALSE at this point, so...
  • setSubMenusOpen() is called which causes a re-render.
  • Instead of previsOpen and props.isOpen now being FALSE and FALSE, they remain unchanged, so setSubMenusOpen gets called again, ad finitum

Any help would be greatly appreciated.

like image 984
jonhobbs Avatar asked Dec 11 '18 19:12

jonhobbs


1 Answers

Problem is that you are setting state directly in render which is causing an infinite cycle of setting state and re-rendering. Instead use the useEffect hook and execute it only on isOpen prop change

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
like image 60
Shubham Khatri Avatar answered Oct 23 '22 03:10

Shubham Khatri