简体   繁体   中英

Open a page in a Next.js website as an overlay

I have a website with a search on it. I'd like the user to be able to click on a search result and open the product in an overlay instead of reloading the whole page, however I would like the url in the url bar to be the correct product page so that is a user copies and pastes it or opens it in a new tab they'll get the product page. I'd also like the back button to work in both cases.

在此处输入图像描述

Here's aa more detailed explanation of the process.

  • User navigates to the list page at /list?keywords=widget
  • User clicks a product
  • The contents of the product animate in on an overlay but the list page doesn't unload, it's still below the product page
  • The URL bar updates to reflect that the user is on /products/123 but the products page isn't actually loaded.
  • If the user hits the back button then the overlay disappears.
  • If the user hits the refresh button while the overlay is open they will get an ssr version of the product page, the list page underneath is no longer there.

I thought at first that I might be able to use the {shallow:true} option on Next/Router to get this to work but the docs specifically say this only works for same page navigation (with a different query string).

My only idea is that I may be able to set the link up as a proper but hijack it in the browser to do something in pure javascript in the history state but I'm not sure whether the Next Router can be removed from the equation.

Just to be clear, I'm not asking for help with the product page or overlay itself, I can easily create shared components that show the same content in both situations, it's specifically the routing and URL issues I'm requesting help with.

Any help, even just a pointer in the right direction would be really appreciated.

Here is how I did it.

You'll need to create an optional catch all route . ie: pages/search/[[searchParams]].tsx

import { useRouter } from 'next/router';
import { GetServerSidePropsContext } from 'next';
import { ProductsView, ProductDetailsView } from '@/views';

export async function getServerSideProps({
  params,
}: GetServerSidePropsContext) {
  const [productId = null] = (params?.searchParams as string[]) || [];
  return {
    props: {
      productId,
    },
  };
}

interface Props {
  productId?: string;
}

function SearchProductsPage({ productId }: Props) {
  const router = useRouter();

  // You could use next/link in your views instead of using next/router
  const onViewDetails = (productDetailsId: string) => {
    router.push(productDetailsId, `product/${productDetailsId}`).catch((e) => {
      console.error(e);
    });
  };
  // Going back to /search will remove the productId and the overlay view will be removed 
  const onClose = () => {
    router.push('/search').catch((e) => {
      console.error(e);
    });
  };

  // If you have a productId (catch from URL and received via Props)
  // it will render the Product details view which contains the overlay and the product details
  return (
    <>
      <ProductsView onViewDetails={onViewDetails} />
      {productId && <ProductDetailsView productId={productId} onClose={onClose} />}
    </>
  );
}

export default SearchProductsPage;

Please note that routing to /product/${productDetailsId} instead of product/${productDetailsId} will give you an error. So you with this code, you'll end up with URL like /search/product/1 instead of /product/1 .

But you can create a separate page ie pages/product/[productId].tsx that renders a product details view, not in an overlay.

I have made a code sandbox for you, hope that helps.

https://codesandbox.io/s/overlay-4qhfb-4qhfb

We have used two React Hooks, one to keep state and one to handle the event. We used window.history.pushState to handle the URL

I don't know any Next.js, but could you just use History.pushState() combined with a modal?

Can you add the state as you push the URL? So you can keep track of the previous URL.

history.push({
  pathname: PATH_NAME,
  state: {
    from: PREV_URL
  },
});

I know I'm late to the game but I found another way from the NextJS Documentation you might find useful.

In your case you could update your next.config file this way

module.exports = {
  async rewrites() {
    return [
      {
        source: '/product/:id*',
        destination: '/search', // The :id parameter isn't used here so will be automatically passed in the query
      },
    ]
  },
}

Then within search page you can access the id of the product this way

const router = useRouter();
const { query } = router;
const productID = query?.id;

Depending on whether the product id is available or not you can display or hide the product modal/overlay.

Doing it this way reduces the code overhead and you will also be able to use next links normally without manually changing the route

Hope this helps. Cheers!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM