skip to Main Content

I am using Next.js 14 with the app router. I have two routes, /businesses and /people. Both have a top-level React Server Component Page which fetches the respective list data for each page. Each Page then renders a client side component Content, which takes the list data and hydrates a React Context for that page with the data, called DataContext with its respective provider DataProvider.

// app/people/page.tsx
const Page = () => {
  const people = getPeople();

  return <Content entities={people} />;
};

// app/people/content.tsx
const Content = ({ entities }) => {
  const sortAndFilter = useSortAndFilter();
  const { filters, pagination, sort } = sortAndFilter;
  const searchableFields = ['name', 'role', 'businessName'];
  const data = useFilteredEntities({ filters, pagination, sort, entities, searchableFields })

  return (
    <SortAndFilterProvider value={sortAndFilter}>
      <DataProvider value={data}>
        <SearchPage />
      </DataProvider>
    </SortAndFilterProvider>
  )
};

I have verified the default 30 second client side Router Cache works by both logging on the server and looking at the network tab when moving between routes.

It appears that the React Context is being destroyed and re-created when navigating back to /people, as the list momentarily flashes in an empty state when navigating back to this page. This happens even though the page itself appears to be fully cached on the client side. I am wondering if it is possible to keep the React Context alive inside of the Router Cache somehow?

2

Answers


  1. Define a context file outside:

        // context/MyContext.js
    import React, { createContext, useState } from 'react';
    
    export const MyContext = createContext();
    
    export const MyProvider = ({ children }) => {
      const [state, setState] = useState(initialState);
    
      return (
        <MyContext.Provider value={{ state, setState }}>
          {children}
        </MyContext.Provider>
      );
    };
    

    Wrap the app with the provider created:

        // pages/_app.js
    import { MyProvider } from '../context/MyContext';
    
    function MyApp({ Component, pageProps }) {
      return (
        <MyProvider>
          <Component {...pageProps} />
        </MyProvider>
      );
    }
    
    export default MyApp;
    

    Now, we can access the context state in the components:

        // pages/index.js
    import { useContext } from 'react';
    import { MyContext } from '../context/MyContext';
    
    const HomePage = () => {
      const { state, setState } = useContext(MyContext);
    
      return (
        <div>
          <h1>Home Page</h1>
          <pre>{JSON.stringify(state, null, 2)}</pre>
          <button onClick={() => setState({ ...state, newValue: 'Updated!' })}>
            Update State
          </button>
        </div>
      );
    };
    
    export default HomePage;
    
    Login or Signup to reply.
    1. perhaps if you use next.js built in middleware https://nextjs.org/docs/pages/building-your-application/routing/middleware and extend client side caching by modifing headers into longer then 30s time spams;
    2. as for keeping React Context alive in the browser cache if the option above dosent work you can store the data in localstorage or cookies but the whole point of using client side state (like React Context) is that you can control and reflect in UI any changes to that state, by ‘freezing’ it in cache (or local storage) all those benefits are gone.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search