skip to Main Content

I have a nested dynamic page structure using catchall routes in Nextjs App Router as follows:

app/stay/
    |__[...category]
    |    |__page.js
    |__[uid]
    |   |__page.js
    |__layout.js

I have a context provider in the ‘layout.js’ which holds data for a filtering function and wraps the pages in […category] and [uid]:

export default async function StayLayout({ children }) {
  
  const client = createClient();
    const stay_pages = await client.getAllByType('stay_page',
    {
      fetchLinks: ["location_category.category_title", "stay_category.category_title"],
      orderings: [
        {
          field: "my.stay_page.listing_title",
          direction: "asc",
        },
      ],
    },
  )

  const filteredStayListings = Array.from(
    new Set(stay_pages.filter((item) => item.data.stay_category.uid === 'guest-ranches-lodges'))
  )

    return (
    <StayProvider 
      filteredStayListings={filteredStayListings}
    >
    {children}
  </StayProvider>
    )
}

Which renders a structure: /stay/guest-ranches-lodges/lodge-name

The problem I’m facing, is that I need to dynamically evaluate ‘guest-ranches-lodges’ in the filteredStayListings function with the page’s UID instead of the hard-coded string shown in my example.

According to the Nextjs docs:

Passing data between a parent layout and its children is not possible.
However, you can fetch the same data in a route more than once, and
React will automatically dedupe the requests without affecting
performance.

Since my data needs to be filtered according to UID before being defined in React Context, I need to evaluate it based off the UID or pathname. I can’t find an available method to find the page’s UID since the only available parameter is {children}.

Is there a way to accomplish this, or a better pattern to possibly override my context?

2

Answers


  1. Chosen as BEST ANSWER

    I've refactored some of my code so that I'm no longer fetching data from a nested layout, but rather pages so I have access the the UID.

    I currently have context providers wrapping my app in the RootLayout (/app/layout.js), but the initial state of my 'stayListings' variable is simply an empty array since I don't have that data until we render '/stay/[uid]/page.js. I'm updating the context with fetched data in a page file as shown here:

    import StayContextProvider from "@/context/StayFilterContext";
    import { createClient } from "@/prismicio";
    import { SliceZone } from '@prismicio/react';
    import { components } from '@/slices';
    import StayCategoryPageContent from "@/components/StayPages/StayCategoryPageContent/StayCategoryPageContent";
    
    export async function generateStaticParams() {
      const client = createClient()
    
      const pages = await client.getAllByType('stay_category_page')
      return pages.map((page) => {
        
        return {
          uid: page.uid,
        }
      })
    }
    
    export default async function Page({ params }) {
      const client = createClient();
      const page = await client.getByUID("stay_category_page", params.uid, 
      );
    
      const stay_pages = await client.getAllByType('stay_page',
        {
          fetchLinks: ["location_category.category_title", "stay_category.category_title"],
          orderings:[
            {
              field: "my.stay_page.uid",
              direction: "asc",
            },
          ]
        },
      )
    
    
    const filteredStayListings =  stay_pages.filter((item) =>
    item.data.stay_categories.some(
      (category) => category.stay_category.uid === params.uid
      )
    );
      
    
    return (
        <StayContextProvider
          stayListings={filteredStayListings}
        >
          <main>
              <StayCategoryPageContent
                page={page}
                stayListings={filteredStayListings}
              />
              <SliceZone slices={page.data.slices} components={components} />
          </main>
        </StayContextProvider>
      )
    }
    

    This pattern I think is causing the issue of context being reset, but cannot figure out how this can be accomplished with server components.

    Basically, the context values are being reset to this initial empty array on subsequent routes changes after my frontend code has updated the context, shown below:

    StayFilterContext.jsx

    'use client';
    
    import { createContext, useState, useEffect, useRef } from 'react';
    
    export const StayContext = createContext();
    
    function StayContextProvider({ children, stayListings }) {
    
        const [locationChecked, setLocationChecked] = useState('all');
        const [filteredData, setFilteredData] = useState(stayListings);
        const [locationList, setLocationList] = useState([]);
    
        useEffect(() => {
          setFilteredData(stayListings);
        }, [stayListings]);
    
       
        const scrollRef = useRef({
            scrollPos: 0,
        })
    
        return (
            <StayContext.Provider value={{ locationChecked, setLocationChecked, filteredData, setFilteredData, locationList, setLocationList, scrollRef: scrollRef }}>
                {children}
            </StayContext.Provider>
        )
    }
    
    export default StayContextProvider;
    

    I've tried moving the from the RootLayout to wrap the individual pages, but with the same result.


  2. Define a function setFilteredStayListings within the context object and then invoke the function from the child component to update the filtered values.

    Check out this question: How to update React Context from inside a child component?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search