如何在 window 重新加载 nextJS 后保留状态变量的值?

How to persist the value of state variables after window reloads in nextJS?

简介

我正在用 nextJS 设计一个电子商务平台。它由这样的 T 恤页面组成。

单击四件 T 恤中的任何一件后,我转到了 slug.js 文件中的特定 T 恤页面,页面如下所示。

这就是我面临的问题。

问题

可以看到黑色T恤页面的URL是

http://localhost:3000/products/wear-the-chess-formula-S-Black

这是正确的,但在那之后如果我 select 任何颜色和尺寸变体,比如 (Green,M),它不会在重新加载后反映在 URL 中。您还可以在下图中看到左上角。重新加载后我又看到了黑色 T 恤图像。

为了这个功能,我构建了以下函数。

  const refreshVariant = (newsize, newcolor) => {
    let url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`;
    //console.log("url = ",url, "newcolor - ",newcolor, "newsize - ", newsize)
    //console.log("newvaraint -", colorSizeSlug)
    window.location = url;
  };

我怎么想的?

我认为点击颜色按钮和尺寸下拉选项,颜色和尺寸的值发生变化,但在重新加载后,它又回到了初始值。

现场行为

我想要什么?

我想保留这两个状态变量的值 color & size,这样每次我们 select 颜色时,相同颜色的点就会突出显示并且同样反映在 URL 中。例如,如果我 select 绿色和 M 尺寸,那么绿色点应该用边框突出显示,这个 URL 应该出现

http://localhost:3000/products/wear-the-chess-formula-M-Green

代码

负责这个的文件是slug.js

import React, { useState } from "react";
import { useRouter } from "next/router";
import mongoose from "mongoose";
import Product from "../../models/Product";

//colosizeSlug :
//  {
//     Black: {S: {slug: "wear-the-chess-formula-1"}},
//     Blue: {M: {slug: "wear-the-chess-formula-2"}},
//     Green: {L: {slug: "wear-the-chess-formula-3"}},
//     Red: {XL: {slug: "wear-the-chess-think-1"} ,XXL: {slug: "wear-the-chess-think-2"}}
//  }

// MY LOGS

//date - 17-4-22
//issue of setColor & setSize is resolved but now the window reload functon disabled. It is beacuse when the page reloads , the state value of color and size revert back to the its initial value & not the current value. That's why you can see the updated url is not displayed in chrome tab.

const Slug = ({ addToCart, all_Tshirts, colorSizeSlug }) => {
  //console.log(all_Tshirts, colorSizeSlug);

  //console.log("keys = ",Object.keys(colorSizeSlug["Red"]))

  const router = useRouter();
  const { slug } = router.query;
  const [pin, setPin] = useState();
  const [isAvailable, setIsAvailable] = useState();

  const [color, setColor] = useState(all_Tshirts[0].color);
  const [size, setSize] = useState(all_Tshirts[0].size);

  console.log("size =", size, " color = ", color);

  const checkservicibilty = async () => {
    let pins = await fetch("http://localhost:3000/api/pincode");
    let pinjson = await pins.json();
    //console.log(pin, pinjson);
    if (pinjson.includes(pin)) {
      setIsAvailable(true);
      //console.log("Available");
    } else {
      setIsAvailable(false);
      //console.log("NOt");
    }
  };

  const onChangePin = (e) => {
    setPin(e.target.value);
  };

  const refreshVariant = (newsize, newcolor) => {
    let url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`;
    //console.log("url = ",url, "newcolor - ",newcolor, "newsize - ", newsize)
    //console.log("newvaraint -", colorSizeSlug)
    window.location = url;
  };

  return (
    <>
      <section className="text-gray-600 body-font overflow-hidden">
        <div className="container px-5 py-16 mx-auto">
          <div className="lg:w-4/5 mx-auto flex flex-wrap">
            <img
              alt="ecommerce"
              className="lg:w-1/2 w-full h-80 lg:h-[36rem] object-cover object-top rounded"
              src={`${colorSizeSlug[color][size]["url"]}`}
            />
            <div className="lg:w-1/2 w-full lg:pl-10 lg:py-6 mt-6 lg:mt-0">
              <h2 className="text-sm title-font text-gray-500 tracking-widest">
                CHESS WEAR
              </h2>
              <h1 className="text-gray-900 text-3xl title-font font-medium mb-1">
                Men&apos;s Chess Formula T-Shirt ( L-Black )
              </h1>
              <div className="flex mb-4">
                <span className="flex items-center">
                 .
                 .
                 .
                  <span className="text-gray-600 ml-3">4 Reviews</span>
                </span>
                <span className="flex ml-3 pl-3 py-2 border-l-2 border-gray-200 space-x-2s">
                 .
                 .
                 .
                 
                </span>
              </div>
              <p className="leading-relaxed">
                Fam locavore kickstarter distillery. Mixtape chillwave tumeric
                sriracha taximy chia microdosing tilde DIY. XOXO fam indxgo
                juiceramps cornhole raw denim forage brooklyn. Everyday carry +1
                seitan poutine tumeric. Gastropub blue bottle austin listicle
                pour-over, neutra jean shorts keytar banjo tattooed umami
                cardigan.
              </p>
              <div className="flex mt-6 items-center pb-5 border-b-2 border-gray-100 mb-5">
                <div className="flex">
                  <span className="mr-3">Color</span>
                  {Object.keys(colorSizeSlug).includes("Red") &&
                    Object.keys(colorSizeSlug["Red"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Red");
                          setColor("Red");
                        }}
                        className={`border-2 bg-red-600 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Red" ? "border-black" : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Green") &&
                    Object.keys(colorSizeSlug["Green"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Green");
                          setColor("Green");
                        }}
                        className={`border-2 ml-1 bg-green-600 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Green" ? "border-black" : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Black") &&
                    Object.keys(colorSizeSlug["Black"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Black");
                          setColor("Black");
                        }}
                        className={`border-2 ml-1 bg-black rounded-full w-6 h-6 focus:outline-none ${
                          color === "Black" ? "border-black" : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("White") &&
                    Object.keys(colorSizeSlug["White"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "White");
                          setColor("White");
                        }}
                        className={`border-2 bg-white rounded-full w-6 h-6 focus:outline-none ${
                          color === "White" ? "border-black" : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Yellow") &&
                    Object.keys(colorSizeSlug["Yellow"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Yellow");
                          setColor("Yellow");
                        }}
                        className={`border-2 bg-yellow-500 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Yellow"
                            ? "border-black"
                            : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Purple") &&
                    Object.keys(colorSizeSlug["Purple"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Purple");
                          setColor("Purple");
                        }}
                        className={`border-2 bg-purple-600 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Purple"
                            ? "border-black"
                            : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Maroon") &&
                    Object.keys(colorSizeSlug["Maroon"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Maroon");
                          setColor("Maroon");
                        }}
                        className={`border-2 bg-rose-700 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Maroon"
                            ? "border-black"
                            : "border-gray-300"
                        }`}
                      ></button>
                    )}
                  {Object.keys(colorSizeSlug).includes("Blue") &&
                    Object.keys(colorSizeSlug["Blue"]).includes(size) && (
                      <button
                        onClick={() => {
                          refreshVariant(size, "Blue");
                          setColor("Blue");
                        }}
                        className={`border-2 bg-blue-500 rounded-full w-6 h-6 focus:outline-none ${
                          color === "Blue" ? "border-black" : "border-gray-300"
                        }`}
                      ></button>
                    )}
                </div>
                <div className="flex ml-6 items-center">
                  <span className="mr-3">Size</span>
                  <div className="relative">
                    <select
                      value={size}
                      onChange={(e) => {
                        refreshVariant(e.target.value, color);
                        setSize(e.target.value);
                      }}
                      className="rounded border appearance-none border-gray-300 py-2 focus:outline-none focus:ring-2 focus:ring-blue-200 focus:border-blue-500 text-base pl-3 pr-10"
                    >
                      {Object.keys(colorSizeSlug[color]).includes("S") && (
                        <option value={"S"}>S</option>
                      )}
                      {Object.keys(colorSizeSlug[color]).includes("M") && (
                        <option value={"M"}>M</option>
                      )}
                      {Object.keys(colorSizeSlug[color]).includes("L") && (
                        <option value={"L"}>L</option>
                      )}
                      {Object.keys(colorSizeSlug[color]).includes("XL") && (
                        <option value={"XL"}>XL</option>
                      )}
                      {Object.keys(colorSizeSlug[color]).includes("XXL") && (
                        <option value={"XXL"}>XXL</option>
                      )}
                    </select>
                    <span className="absolute right-0 top-0 h-full w-10 text-center text-gray-600 pointer-events-none flex items-center justify-center">
                      <svg
                        fill="none"
                        stroke="currentColor"
                        strokeLinecap="round"
                        strokeLinejoin="round"
                        strokeWidth="2"
                        className="w-4 h-4"
                        viewBox="0 0 24 24"
                      >
                        <path d="M6 9l6 6 6-6"></path>
                      </svg>
                    </span>
                  </div>
                </div>
              </div>
              <div className="flex">
                <span className="title-font font-medium text-2xl text-gray-900">
                  ₹499
                </span>
                <button
                  onClick={() =>
                    addToCart(
                      slug,
                      1,
                      499,
                      "wear the chess(XL, Red)",
                      "XL",
                      "Red"
                    )
                  }
                  className="flex ml-auto md:ml-24 text-sm lg:text-base text-white bg-blue-500 border-0 py-2 px-4 lg:px-6 focus:outline-none hover:bg-blue-600 rounded"
                >
                  Add to Cart
                </button>
                <button className="flex ml-1 md:ml-2 text-white text-sm lg:text-base bg-blue-500 border-0 py-2 px-4 lg:px-6 focus:outline-none hover:bg-blue-600 rounded">
                  Buy Now
                </button>
                <button className="rounded-full w-10 h-10 bg-gray-200 p-0 border-0 inline-flex items-center justify-center text-gray-500 ml-4 md:ml-6">
                  <svg
                    fill="currentColor"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth="2"
                    className="w-5 h-5"
                    viewBox="0 0 24 24"
                  >
                    <path d="M20.84 4.61a5.5 5.5 0 00-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 00-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 000-7.78z"></path>
                  </svg>
                </button>
              </div>
              <div className="pin mt-6 space-x-2 flex text-sm">
                <input
                  type="text"
                  id="name"
                  name="name"
                  onChange={onChangePin}
                  placeholder="Enter Pincode Here"
                  className="w-3/5 md:w-2/5 bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
                />
                <button
                  onClick={checkservicibilty}
                  className="text-white bg-blue-500 border-0 py-2 px-6 focus:outline-none hover:bg-blue-600 rounded"
                >
                  Check
                </button>
              </div>

              {isAvailable != null ? (
                isAvailable ? (
                  <div className="text-sm mt-4 text-green-600">
                    Yay ! This pincode is serviceable .
                  </div>
                ) : (
                  <div className="text-sm mt-4 text-red-600">
                    Sorry ! We do not deliver to this pincode yet .
                  </div>
                )
              ) : (
                <div></div>
              )}
            </div>
          </div>
        </div>
      </section>
    </>
  );
};

// taking all the variants of a tshirt/hoodies of same title from the slug
// for eg, if slug=wear-chess-think-2, then it will give all the 'Chess-Think Tshirts'=title tshirts in variant-array
export async function getServerSideProps(context) {
  if (!mongoose.connections[0].readyState) {
    await mongoose.connect(process.env.MONGO_URI);
  }
  let product = await Product.findOne({ slug: context.query.slug });
  let all_Tshirts = await Product.find({ title: product.title });
  //all tshirts of same title with the given 'slug'
  //console.log("all -",all_Tshirts)
  let colorSizeSlug = {};
  //eg: {'Red': {'XL':'wear-the-chess-king-1}}
  for (let item of all_Tshirts) {
    if (Object.keys(colorSizeSlug).includes(item.color)) {
      colorSizeSlug[item.color][item.size] = { slug: item.slug, url: item.img };
    } else {
      colorSizeSlug[item.color] = {};
      colorSizeSlug[item.color][item.size] = { slug: item.slug, url: item.img };
    }
  }
  return {
    props: {
      all_Tshirts: JSON.parse(JSON.stringify(all_Tshirts)),
      colorSizeSlug: JSON.parse(JSON.stringify(colorSizeSlug)),
    }, // will be passed to the page component as props
  };
}

export default Slug;

而不是使用 window.location 你需要使用 router.push

https://nextjs.org/docs/api-reference/next/router#routerpush

这里的关键是使用router push,shallow设置为true,避免页面重渲染。

const refreshVariant = (newsize, newcolor) => {
  const url = `http://localhost:3000/products/${colorSizeSlug[newcolor][newsize]["slug"]}`
  router.push(url, undefined, { shallow: true })
}