我已经开始在一个项目上使用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以使用自定义下拉列表,但库不起作用。
1条答案
按热度按时间ddrv8njm1#
同样的问题。使用useState和useEffect修复。
https://traviswimer.com/blog/error-hydration-failed-because-the-initial-ui-does-not-match-what-was-rendered-on-the-server/