next.js 下一个13如何修复水合失败,因为初始UI与服务器上呈现的内容不匹配时使用自定义下拉菜单

brgchamk  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(143)

我已经开始在一个项目上使用next 13的新应用程序目录功能。我试图在我的一个页面中使用此下拉列表,我得到此错误如何修复水合失败,因为初始UI不匹配使用自定义下拉列表时在服务器上呈现的内容。
这是我的下拉代码

import { useState, useEffect, useRef, CSSProperties } from "react";

interface DropdownOption {
  label: string;
  value: string;
}
interface DropdownProps {
  value: string;
  options: DropdownOption[];
  onChange: any;
  style?: CSSProperties;
  loading?: boolean;
  placeholder?: string;
  ariaLabel?: string;
  disabled?: boolean;
  allowClear?: boolean;
  onClear?: () => void;
}

const Dropdown = ({
  options,
  value,
  style,
  onChange,
  loading,
  placeholder,
  ariaLabel,
  disabled,
  allowClear = false,
  onClear,
}: DropdownProps) => {
  const [isOpen, setIsOpen] = useState(false);
  const [searchText, setSearchText] = useState("");
  const [showClearButton, setShowClearButton] = useState(allowClear);
  const [dropdownValue, setDropdownValue] = useState(value || "");
  const [dropdownLabel, setDropdownLabel] = useState(
    options.find((item) => item.value === value)?.label || ""
  );

  const dropdownRef = useRef(null);
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      // @ts-ignore
      if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
        setIsOpen(false);
      }
    };

    window.addEventListener("click", handleClickOutside);

    return () => {
      window.removeEventListener("click", handleClickOutside);
    };
  }, []);

  useEffect(() => {
    if (dropdownLabel.trim() !== "") {
      setShowClearButton(true);
    }
  }, [dropdownLabel]);
  const filteredOptions = options.filter((option) =>
    option.label.toLowerCase().includes(searchText.toLowerCase())
  );

  const handleButtonClick = () => {
    setSearchText("");
    setIsOpen(!isOpen);
  };

  const handleOptionClick = (option: DropdownOption) => {
    console.log(option);
    onChange(option.value);
    setDropdownValue(option.value);
    setDropdownLabel(option.label);

    setSearchText("");
    setIsOpen(false);
  };
  console.log(searchText);
  console.log("drop value", dropdownValue);
  console.log("drop label", dropdownLabel);

  const handleSearch = (e: any) => {
    setDropdownLabel(e.target.value);
    setSearchText(e.target.value);
  };

  const clearSelectHandler = () => {
    setDropdownValue("");
    setDropdownLabel("");
    setShowClearButton(false);
    onClear?.();
  };

  if (loading) {
    return <div>Loading dropdown...</div>;
  }

  if (disabled) {
    return (
      <div
        aria-label={ariaLabel && ariaLabel}
        style={style}
        className="relative"
      >
        <button
          className="bg-gray-200 border border-gray-300 rounded-md shadow-sm py-2 px-4 flex items-center justify-between w-full text-gray-500"
          disabled
        >
          <input
            type="text"
            className="  outline-0    block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm "
            placeholder={placeholder}
            value={dropdownLabel}
            onChange={handleSearch}
            disabled
          />
          <svg
            className="h-5 w-5 text-gray-500"
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fillRule="evenodd"
              d="M10 14.293l-5.146-5.147a.5.5 0 01.708-.708L10 12.586l4.439-4.439a.5.5 0 11.708.708l-5.146 5.147a.5.5 0 01-.708 0z"
              clipRule="evenodd"
            />
          </svg>
        </button>
      </div>
    );
  }

  return (
    <div
      aria-label={ariaLabel && ariaLabel}
      style={style}
      ref={dropdownRef}
      className="relative"
    >
      <button
        className="bg-white border border-gray-300 rounded-md shadow-sm py-2 px-4 flex items-center justify-between w-full text-gray-700"
        onClick={handleButtonClick}
      >
        <input
          type="text"
          className="  outline-0      block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm "
          placeholder={placeholder}
          value={dropdownLabel}
          onChange={handleSearch}
        />
        {showClearButton ? (
          <svg
            onClick={clearSelectHandler}
            className="h-4 w-4 text-gray-700 z-10"
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 100 100"
            strokeWidth="2"
          >
            <path d="M 10,10 L 90,90 M 10,90 L 90,10" stroke="black" />
          </svg>
        ) : (
          <svg
            className="h-5 w-5 text-gray-500"
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fillRule="evenodd"
              d="M10 14.293l-5.146-5.147a.5.5 0 01.708-.708L10 12.586l4.439-4.439a.5.5 0 11.708.708l-5.146 5.147a.5.5 0 01-.708 0z"
              clipRule="evenodd"
            />
          </svg>
        )}
      </button>
      {isOpen && (
        <div className="absolute z-10 bg-white border border-gray-300 rounded-md shadow-lg py-1  w-full">
          <ul>
            {filteredOptions.map((option) => (
              <li
                key={option.value}
                className="cursor-pointer px-4 py-2 hover:bg-gray-100"
                onClick={() => handleOptionClick(option)}
              >
                {option.label}
              </li>
            ))}
            {filteredOptions.length === 0 && (
              <div className="px-2 text-slate-500">No options left.</div>
            )}
          </ul>
        </div>
      )}
    </div>
  );
};

export default Dropdown;

我正在使用我的一个页面中的代码,如下所示

"use client";
import Input from "@/components/Input";
import React from "react";
import Button from "@/components/Button";
import Dropdown from "@/components/Dropdown";
const Recommend = () => {
  const eventHandler = (e: string) => {
    console.log(e);
  };
  return (
    <div className="h-screen flex flex-col bg-primary items-center">
      <h2 className=" w-full p-4 text-white text-center text-2xl">
        Configure your recommendations settings
      </h2>

      <div className=" mb-10 text-textPrimary text-center">
        <h3 className="text-bold pb-4 text-lg">Guide</h3>
        <div>
          Up to 5 seed values may be provided in any combination of artists,
          tracks and genres.
        </div>
      </div>

      <div className="max-w-2xl ">
        <Input
          placeholder="Track"
          labelPosition="center"
          label="Track"
          onChange={eventHandler}
        />
        <Input
          placeholder="Artist"
          labelPosition="center"
          label="Artist"
          onChange={eventHandler}
        />

        <p className="text-red-500 text-center p-4">Error</p>
        <Button
          className="bg-tertiary rounded-md py-2  w-full text-center"
          text="Search"
          onClick={() => console.log("respect")}
        />

        <Dropdown
          options={[
            { label: "Banana", value: "banana" },
            { label: "Cherry", value: "cherry" },
            { label: "Durian", value: "durian" },
            { label: "Elderberry", value: "elderberry" },
            { label: "Fig", value: "fig" },
          ]}
          onChange={eventHandler}
          value={"banana"}
          loading={false}
          placeholder={"test"}
          onClear={() => {
            console.log("respect");
          }}
        />
      </div>
    </div>
  );
};

export default Recommend;

我正在使用“使用客户端”,为什么我仍然得到这个水合错误?
我也试过用这个

const DynamicDropdown = dynamic(
  async () => import("../../components/Dropdown"),
  { ssr: false }
);

尝试在每个组件中使用use client,尝试删除状态,尝试使用ant design以使用自定义下拉列表,但库不起作用。

相关问题