0

This is my header component --

import { isEmpty } from "lodash";
import Nav from "./Nav";

const Header = ({ headerMenus }) => {
  // console.log(headerMenus); <- Here i get the menus
  if (isEmpty(headerMenus)) {
    return null;
  }
  return (
    <Header>
      <Nav headerMenus = {headerMenus}/>
    </Header>
  );
};
export default Header;

when I send data through props to nav I am not getting any data in nav component please help here the code of Nav

const Nav = ({headerMenus}) =>{
  // console.log(headerMenus); <~~ Not getting data here so i can't return anything through this 

  return;
};
export default Nav;

1 Answers1

0

are you using WPGraphQL as your endpoint? you also more than likely need to have a bearer token unless your endpoint can be publicly introspected. Ill post a few files here from a current headless wordpress build I have

First, consider using something like apollo client to provide consistent caching globally. SWR and GraphQL Query are fantastic, too.

dynamic-nav.graphql

# import DynamicNavFragment from '../Partials/dynamic-nav-fields.graphql'

query DynamicNav(
    $idHead: ID!
    $idTypeHead: WordpressMenuNodeIdTypeEnum!
    $idFoot: ID!
    $idTypeFoot: WordpressMenuNodeIdTypeEnum!
) {
    Header: wordpress {
        menu(id: $idHead, idType: $idTypeHead) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                    childItems {
                                        edges {
                                            node {
                                                ...DynamicNavFragment
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    Footer: wordpress {
        menu(id: $idFoot, idType: $idTypeFoot) {
            menuItems(where: { parentId: 0 }) {
                edges {
                    node {
                        ...DynamicNavFragment
                        childItems {
                            edges {
                                node {
                                    ...DynamicNavFragment
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

The fragment being used is

dynamic-nav-fields.graphql

fragment DynamicNavFragment on WordpressMenuItem {
    id
    label
    path
    parentId
}

It all comes together in a global layout.tsx component. There is a lengthy headless-nav component I'll include below but that's because it is 3 layers deep with dynamic subsubanchors

layout.tsx

import { FooterFixture } from './Footer';
import { HeadlessFooter } from './HeadlessFooter';
import { HeadlessNavbar } from './HeadlessNavbar';
import { Navbar } from './Navbar';
import { Meta } from './Meta';
import cn from 'classnames';
import {
    DynamicNavQuery,
    useDynamicNavQuery,
    WordpressMenuNodeIdTypeEnum
} from '@/graphql/generated/graphql';
import {
    ApolloError,
    Button,
    Fallback,
    Modal,
    LandingHero
} from '../UI';
import { useGlobal } from '../Context';
import { useAcceptCookies } from '@/lib/use-accept-cookies';
import Head from 'next/head';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
// import { LoginProps } from '../Auth/wp-login';

const dynamicProps = {
    loading: () => <Fallback />
};

const LoginView = dynamic(
    () => import('../Auth/wp-login'),
    dynamicProps
);

const RegisterView = dynamic(
    () => import('../Auth/wp-register'),
    dynamicProps
);

const Featurebar = dynamic(
    () => import('./Featurebar'),
    dynamicProps
);

export interface LayoutProps {
    Header: DynamicNavQuery['Header'];
    Footer: DynamicNavQuery['Footer'];
    className?: string;
    title?: string;
    hero?: React.ReactNode;
    children?: React.ReactNode;
}

function AppLayout({
    Header,
    Footer,
    className,
    title,
    children,
    hero
}: LayoutProps) {
    const { acceptedCookies, onAcceptCookies } = useAcceptCookies();
    const { displayModal, modalView, closeModal } = useGlobal();
    const { loading, error, data } = useDynamicNavQuery({
        variables: {
            idHead: 'Header',
            idTypeHead: WordpressMenuNodeIdTypeEnum.NAME,
            idTypeFoot: WordpressMenuNodeIdTypeEnum.NAME,
            idFoot: 'Footer'
        },
        notifyOnNetworkStatusChange: true
    });
    const router = useRouter();
    Header =
        data != null && data.Header != null ? data.Header : undefined;
    Footer =
        data != null && data.Footer != null ? data.Footer : undefined;

    return (
        <>
            <Head>
                <title>{title ?? '✂ The Fade Room Inc. ✂'}</title>
            </Head>
            <Meta />
            {error ? (
                <>
                    <ApolloError error={error} />
                </>
            ) : loading && !error ? (
                <Fallback />
            ) : (
                <Navbar
                    Desktop={<HeadlessNavbar header={Header} />}
                    Mobile={
                        <HeadlessNavbar
                            header={Header}
                            className={
                                'block px-3 py-2 rounded-md text-lg sm:text-base font-sans font-semibold text-secondary-0 hover:bg-redditSearch'
                            }
                        />
                    }
                />
            )}
            <>
                {router.pathname === '/' ? <LandingHero /> : hero}
                <Modal open={displayModal} onClose={closeModal}>
                    {modalView === 'LOGIN_VIEW' && <LoginView />}
                    {modalView === 'SIGNUP_VIEW' && <RegisterView />}
                </Modal>
                <div className={cn('bg-redditSearch', className)}>
                    <main className='fit'>
                        {children}
                        {error ? (
                            <>
                                <ApolloError error={error} />
                            </>
                        ) : loading && !error ? (
                            <Fallback />
                        ) : (
                            <FooterFixture
                                children={<HeadlessFooter footer={Footer} />}
                            />
                        )}
                    </main>
                    <div className='font-sans z-150'>
                        <Featurebar
                            title='This site uses cookies to improve your experience. By clicking, you agree to our Privacy Policy.'
                            hide={
                                acceptedCookies ? !!acceptedCookies : !acceptedCookies
                            }
                            className='prose-lg sm:prose-xl bg-opacity-90 sm:text-center'
                            action={
                                <Button
                                    type='submit'
                                    variant='slim'
                                    className='mx-auto text-secondary-0 text-center rounded-xl border-secondary-0 border-1 hover:bg-gray-700 hover:bg-opacity-80 hover:border-secondary-0 duration-500 ease-in-out transform-gpu transition-colors'
                                    onClick={() => onAcceptCookies()}
                                >
                                    Accept Cookies
                                </Button>
                            }
                        />
                    </div>
                </div>
            </>
        </>
    );
}

export default AppLayout;

The HeadlessNavbar component being injected with data from the useDynamicNavQuery function is mapped out here. I have been meaning to fractionate this apart but it is a bit of a pain.

Layout/HeadlessNavbar/headless-navbar.tsx

import { useState, FC, useRef, useEffect } from 'react';
import cn from 'classnames';
import { useRouter } from 'next/router';
import Link from 'next/link';
import { Button } from '../../UI';
import { Transition, Listbox } from '@headlessui/react';
import { NavArrow } from '@/components/UI/Icons';
import { DynamicNavQuery } from '@/graphql/generated/graphql';
import css from './headless-navbar.module.css';
import { DynamicNavSubAnchors } from '@/types/dynamic-nav';
import throttle from 'lodash.throttle';

export interface HeadlessNavbarProps
    extends DynamicNavSubAnchors {
    header: DynamicNavQuery['Header'];
    className?: string;
}

const HeadlessNavbar: FC<HeadlessNavbarProps> = ({
    header,
    className,
    node
}) => {
    const [isOpen, setIsOpen] = useState(false);
    const [subOpen, setSubOpen] = useState(false);
    const [hasScrolled, setHasScrolled] = useState(false);
    const [selectedCategory, setSelectedCategory] = useState(node);
    const { pathname } = useRouter();
    const isOpenRef = useRef(isOpen);
    const refAcceptor =
        useRef() as React.MutableRefObject<HTMLDivElement>;

    useEffect(() => {
        const handleScroll = throttle(() => {
            const offset = window.scrollY ?? 0;
            const { scrollTop } = document.documentElement;
            const scrolled = scrollTop > offset;
            setHasScrolled(scrolled);
            setIsOpen(isOpenRef.current);
        }, 200);
        document.addEventListener('scroll', handleScroll);
        return () => {
            document.removeEventListener('scroll', handleScroll);
        };
    }, [hasScrolled, isOpenRef.current]);
    return (
        <>
            {header?.menu?.menuItems?.edges &&
            header.menu.menuItems.edges.length > 0 ? (
                header.menu.menuItems.edges.map((top, i) => {
                    return top != null &&
                        top.node != null &&
                        top.node.label != null ? (
                        <>
                            {top.node.childItems?.edges &&
                            top.node.childItems.edges.length !== 0 ? (
                                <>
                                    <Link
                                        href={top.node.path}
                                        as={top.node.path}
                                        passHref
                                        scroll={true}
                                        key={top.node.id}
                                    >
                                        <a
                                            id='top'
                                            className={cn(className, {
                                                [css.activeWithChildren]: pathname === top.node.path,
                                                [css.linkWithChildren]: pathname !== top.node.path
                                            })}
                                        >
                                            <p className='inline-flex'>{top.node.label}</p>
                                        </a>
                                    </Link>
                                    <button
                                        onClick={() => setIsOpen(!isOpen)}
                                        id='sub-menu'
                                        aria-haspopup={true}
                                        aria-expanded={true}
                                        type='button'
                                        className={cn(css.topButton, {
                                            '-rotate-180': isOpen,
                                            'rotate-0': !isOpen
                                        })}
                                    >
                                        <NavArrow className='select-none w-5 h-5' />
                                    </button>
                                </>
                            ) : top.node.childItems?.edges &&
                              top.node.childItems.edges.length < 1 ? (
                                <Link
                                    href={top.node.path}
                                    as={top.node.path}
                                    passHref
                                    scroll={true}
                                    key={top.node.id}
                                >
                                    <a
                                        id='top'
                                        className={cn(className, {
                                            [css.active]: pathname === top.node.path,
                                            [css.link]: pathname !== top.node.path
                                        })}
                                    >
                                        <p>{top.node.label}</p>
                                    </a>
                                </Link>
                            ) : (
                                <></>
                            )}
                            {top.node.childItems != null &&
                            top.node.childItems.edges != null &&
                            top.node.childItems.edges.length > 0 ? (
                                <div className='lg:relative z-150 -ml-2'>
                                    <Transition
                                        show={isOpen}
                                        enter='transition ease-out duration-200 '
                                        enterFrom='transform opacity-0 translate-y-1'
                                        enterTo='transform opacity-100 translate-y-0'
                                        leave='transition ease-in duration-150'
                                        leaveFrom='transform opacity-100 translate-y-0'
                                        leaveTo='transform opacity-0 translate-y-1'
                                    >
                                        <div className={cn(css.transitionAlpha, '')}>
                                            <div
                                                className={css.transitionBeta}
                                                ref={refAcceptor}
                                                role='menu'
                                                aria-orientation='vertical'
                                                aria-labelledby='sub-menu'
                                            >
                                                <div className={css.transitionGamma}>
                                                    {top!.node!.childItems!.edges!.map((sub, j) => {
                                                        return sub != null &&
                                                            sub.node != null &&
                                                            sub.node.label != null &&
                                                            sub.node.parentId != null ? (
                                                            <Listbox
                                                                key={sub.node.path}
                                                                value={selectedCategory}
                                                                onChange={setSelectedCategory}
                                                            >
                                                                {({ open }) => (
                                                                    <>
                                                                        <div className={cn(css.divOpen)}>
                                                                            <p className='text-base font-medium pr-2 bg-redditNav font-sans inline-flex py-2 ml-4'>
                                                                                {sub!.node!.label!}
                                                                                <Listbox.Button
                                                                                    as='button'
                                                                                    aria-haspopup={true}
                                                                                    id='sub'
                                                                                    aria-expanded={true}
                                                                                    onClick={() => setSubOpen(!subOpen)}
                                                                                    className={cn(css.bottomButton, {
                                                                                        ' bg-redditNav  -rotate-180': open,
                                                                                        ' bg-redditNav  rotate-0': !open
                                                                                    })}
                                                                                >
                                                                                    <NavArrow className='select-none w-5 h-5' />
                                                                                </Listbox.Button>
                                                                            </p>
                                                                        </div>
                                                                        <Transition
                                                                            show={open}
                                                                            enter='transition ease-out duration-100'
                                                                            enterFrom='transform opacity-0 scale-95'
                                                                            enterTo='transform opacity-100 scale-100'
                                                                            leave='transition ease-in duration-75'
                                                                            leaveFrom='transform opacity-100 scale-100'
                                                                            leaveTo='transform opacity-0 scale-95'
                                                                        >
                                                                            <Listbox.Options
                                                                                static
                                                                                className='outline-none select-none focus:outline-none'
                                                                            >
                                                                                {sub!.node!.childItems != null &&
                                                                                sub!.node!.childItems.edges != null &&
                                                                                sub!.node!.childItems.edges.length > 0 ? (
                                                                                    sub!.node!.childItems!.edges!.map(
                                                                                        (subsub, k) => {
                                                                                            return subsub != null &&
                                                                                                subsub.node != null &&
                                                                                                subsub.node.label != null &&
                                                                                                subsub.node.parentId != null ? (
                                                                                                <>
                                                                                                    {open && (
                                                                                                        <Listbox.Option
                                                                                                            key={subsub.node.id}
                                                                                                            className={cn(
                                                                                                                css.subsub,
                                                                                                                'lg:text-base text-sm hover:bg-redditSearch font-medium list-none outline-none font-sans'
                                                                                                            )}
                                                                                                            value={subsub!.node!.label}
                                                                                                        >
                                                                                                            <>
                                                                                                                <Link
                                                                                                                    href={subsub!.node!.path}
                                                                                                                    passHref
                                                                                                                    key={k++}
                                                                                                                    shallow={true}
                                                                                                                >
                                                                                                                    <a
                                                                                                                        id='subsub'
                                                                                                                        className={cn(css.subsubanchor)}
                                                                                                                    >
                                                                                                                        {subsub!.node!.label}
                                                                                                                    </a>
                                                                                                                </Link>
                                                                                                            </>
                                                                                                        </Listbox.Option>
                                                                                                    )}
                                                                                                </>
                                                                                            ) : (
                                                                                                <></>
                                                                                            );
                                                                                        }
                                                                                    )
                                                                                ) : (
                                                                                    <></>
                                                                                )}
                                                                            </Listbox.Options>
                                                                        </Transition>
                                                                    </>
                                                                )}
                                                            </Listbox>
                                                        ) : (
                                                            <></>
                                                        );
                                                    })}
                                                </div>
                                            </div>
                                        </div>
                                    </Transition>
                                </div>
                            ) : (
                                <></>
                            )}
                        </>
                    ) : (
                        <></>
                    );
                })
            ) : (
                <></>
            )}
        </>
    );
};

export default HeadlessNavbar;

Here's the live site the code powers

Andrew Ross
  • 1,094
  • 7
  • 16