Search code examples
reactjsreact-hooksreact-bootstrap

Active state in React Bootstrap


There is a nested menu in React Bootstrap and it's working fine except the active collapsible is not open by default. I need to change the in attribute's value in the Collapse to make it work, but only for the active page. I thought I can use hasActiveChild, but I'm not sure how to add into useEffect properly. I only added the important part of the code, I just basically return with renderMenu later in the code.

    const { data, error, isLoading } = useSWR('/api/staticdata', fetcher);
    const router = useRouter();
    const currentRoute = router.pathname;

    const renderMenu = (menu) => {
        const [open, setOpen] = useState(menu.map(() => false));
        const hasActiveChild = (currentItem) => currentItem.child.some(currentItem => currentItem.url === currentRoute);

        useEffect(() => {
            if (menu.length !== open.length) {
                setOpen(menu.map(() => false));
            }
        }, [menu, open]);

        return menu.map((item, index) => (
            <Nav.Item className={item.child && "dropdown"} key={index}>
                {item.url ?
                    <Nav.Link href={item.url} className={(currentRoute === item.url) && "active"}>
                        <span>{item.title}</span>
                    </Nav.Link>
                :
                    <Nav.Link className={hasActiveChild(item) && "active"} onClick={() => {
                        setOpen((open) => open.map((o, i) => {
                            if (i === index) return !o;
                            return o;
                        }));
                    }} aria-controls={`collapse${capitalizeEachWord(item.title)}`} aria-expanded={open[index]} data-bs-toggle="collapse">
                        <span>{item.title}</span>
                    </Nav.Link>
                }
                {item.child && (
                    <Collapse in={open[index]} key={index} data-open={open[index]}>
                        <div id={`collapse${capitalizeEachWord(item.title)}`}>
                            <div className="nav flex-column">
                                {renderMenu(item.child)}
                            </div>
                        </div>
                    </Collapse>
                )}
            </Nav.Item>
        ))
    }

Solution

  • If someone interested:

    const [open, setOpen] = useState(
        menu.map((m, i) => {
            if(m.child) {
                if(m.child.some(child => {return child.url === currentRoute})) return true;
            }
        })
    );
    
    useEffect(() => {
        if (menu.length !== open.length) {
            setOpen(menu.map(() => false));
        }
    }, [menu]);