Shopify / hydrogen

Hydrogen lets you build faster headless storefronts in less time, on Shopify.
https://hydrogen.shop
MIT License
1.44k stars 273 forks source link

QraphQL response from useLoaderData is always undefined on first render #2489

Open jeffspurlock opened 2 months ago

jeffspurlock commented 2 months ago

What is the location of your example repository?

local

Which package or tool is having this issue?

Hydrogen

What version of that package or tool are you using?

2024.7.2

What version of Remix are you using?

2.10.1

Steps to Reproduce

If you modify the default HEADER_QUERY to include a request for two menus with aliases, like so

query Header(
    $country: CountryCode
    $collectionsMenuHandle: String!
    $siteMenuHandle: String!
    $language: LanguageCode
  ) @inContext(language: $language, country: $country) {
    shop {
      ...Shop
    }
    collectionsMenu: menu(handle: $collectionsMenuHandle) {
      ...Menu
    }
    siteMenu: menu(handle: $siteMenuHandle) {
      ...Menu
    }
    announcementBanners: metaobjects(type: "announcement_banner", reverse: true, first:10) {
    nodes {
      fields {
        key
        value
        type
      }
    }
  }

by default, this populates a header object in the loadCriticalData function. In the component of root.tsx, `const data = useRouteLoaderData<RootLoader<('root')' is called, and data is passed into the PageLayout component. In PageLayout, data is destructured in the props

export function PageLayout({
  cart,
  children = null,
  footer,
  header,
  isLoggedIn,
  publicStoreDomain,
}: PageLayoutProps) {...}

inside of which, header is passed into the

component. Note: so far the only thing that has changed from the default scaffolded hydrogen application is adding the second menu and aliases

Now inside

, i'm destructuring the header object in the props as

export function HeaderMenu({
  siteMenu,
  collectionsMenu,
  primaryDomainUrl,
  viewport,
  publicStoreDomain,
}: {
  siteMenu: HeaderProps['header']['siteMenu'];
  collectionsMenu: HeaderProps['header']['collectionsMenu'];
  primaryDomainUrl: HeaderProps['header']['shop']['primaryDomain']['url'];
  viewport: Viewport;
  publicStoreDomain: HeaderProps['publicStoreDomain'];
}) {...}

if i console.log(collectionsMenu), i can see all the data just fine, it has the shape of

{
  id: "gid://........",
 items: [ Array of menu item data ]
} 

but then if i console.log(collectionsMenu.items) it will say its undefined. You can even log them one right after another; collectionsMenu logs correctly with an items property, but collectionsMenu.items or collectionsMenu['items'] logs undefined.

Expected Behavior

I expect to be able to access the properties of the object returned by an asyncronous loader function that is set to await the result before returning it.

Actual Behavior

I cannot useLoaderData to build out my menu structures defined in shopify to populate the menu component on the site.

jeffspurlock commented 2 months ago

So interestingly, if instead of passing collectionsMenu into the header component, if i just instantiate a new data=useLoaderData() inside the component, and access that it works. When its passed as a prop, it stays undefined until second render.

scottdixon commented 2 months ago

Hey @jeffspurlock,

Thanks for the report! Sounds super strange 🤔 I'm unable to replicate, any chance you could provide a demo repo?