How can the cookie value not be the same in the dynamic route?

920 Views Asked by At

I implemented a program with next js , i want to use cookies on dynamic pages , the problem is that the cookie value is inherited in other pages , this happens while each page should have its own cookie.

I want to have a button to set cookie and a Link for navigation between dynamic blog routes , when i set the cookie for example in this path /blog/2 and then i refresh the page to see the cookie in the current page and click on the Link to go to next blog or previous blog , the cookie is inherited in other dynamic blog routes.

  • In this example , the js-cookie module is used.

Example :

// /blog/[blogId]

import Cookies from 'js-cookie';
import Link from 'next/link';
import { useRouter } from 'next/router';
import dynamic from 'next/dynamic';

function SingleBlog() {
    const router = useRouter();
    const { blogId } = router.query;

    return (
        <>
            <Link href={`/blog/${Number(blogId) + 1}`}> next blog </Link>
            <button onClick={() => Cookies.set('name', `blog${blogId}`, { path: `/blog/${blogId}` })}> set </button>
            {Cookies.get('name') && <p>cookie : {Cookies.get('name')}</p>}
        </>
    )
}

export default dynamic(() => Promise.resolve(SingleBlog), { ssr: false });

  • If the page is refreshed, we see the cookie value and if the navigation between the blog pages is inside the program, the cookie value that is displayed is a fixed value in any case.

What is the solution ?

1

There are 1 best solutions below

7
Igor Danchenko On

UPDATE

Upon further investigation, I was able to determine that browsers do NOT refresh document.cookie with path-specific cookies after client-side navigation. Here is a minimal reproducible example:

// pages/blog/[blog].tsx

import * as React from "react";
import Link from "next/link";
import { useRouter } from "next/router";

export default function Page() {
  const router = useRouter();
  const path = router.asPath;
  const blog = router.query.blog;

  const [cookies, setCookies] = React.useState("");

  const refreshCookie = React.useCallback(() => {
    // ignore initial render
    if (blog !== undefined) {
      console.log(window.location.href, document.cookie);
      setCookies(document.cookie);
    }
  }, [blog]);

  React.useEffect(refreshCookie, [refreshCookie]);

  const saveCookie = () => {
    document.cookie = `cookie=${path}; path=${path}; max-age=${30 * 24 * 3600}`;

    refreshCookie();
  };

  if (blog === undefined) return null;

  return (
    <>
      <nav>
        <Link href="/blog/1">Blog 1</Link>
        <Link href="/blog/2">Blog 2</Link>
        <Link href="/blog/3">Blog 3</Link>
      </nav>

      <h1>Blog {blog}</h1>

      <p>document.cookie: {cookies}</p>

      <button type="button" onClick={saveCookie}>
        Set cookie
      </button>
    </>
  );
}

Sandbox - https://codesandbox.io/p/sandbox/stackoverflow-76316769-wlutfv?file=%2Fpages%2Fblog%2F%5Bblog%5D.tsx

I do not have a good explanation of this behavior at this point. However, I can suggest several alternative solutions.

1) Read cookies server-side on each request

This can be implemented either with getServerSideProps under the Pages Router, or with cookies function under the App Router.

Here is an example using the App Router - https://codesandbox.io/p/sandbox/stackoverflow-76316769-app-ocw66i?file=%2Fapp%2Fblog%2F%5Bblog%5D%2FBlog.tsx

2) Fetch cookies client-side from an API route

Here is an example using the App Router - https://codesandbox.io/p/sandbox/stackoverflow-76316769-app-route-y4jcgw?file=%2Fapp%2Fblog%2F%5Bblog%5D%2FBlog.tsx

I hope this helps.


Original answer

(the original answer addressed an obvious issue in the initial OP's code)

The value that you pass as an argument to useState is used only one time during the initial render to determine the initial state. If you want to use an updated cookie value every time props.blog slug changes, you should read cookie value in useEffect and update your state accordingly.