浏览器控制台上的“组件正在将不受控制的输入更改为受控制”

问题描述 投票:0回答:1
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
    at input
    at _c (webpack-internal:///(app-pages-browser)/./components/ui/input.tsx:13:11)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-slot/dist/index.mjs:46:23)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-slot/dist/index.mjs:20:23)
    at eval (webpack-internal:///(app-pages-browser)/./components/ui/form.tsx:124:14)
    at div
    at eval (webpack-internal:///(app-pages-browser)/./components/ui/form.tsx:74:11)
    at Controller (webpack-internal:///(app-pages-browser)/./node_modules/react-hook-form/dist/index.esm.mjs:564:37)
    at FormField (webpack-internal:///(app-pages-browser)/./components/ui/form.tsx:29:14)
    at form
    at FormProvider (webpack-internal:///(app-pages-browser)/./node_modules/react-hook-form/dist/index.esm.mjs:182:13)
    at FormContent (webpack-internal:///(app-pages-browser)/./components/create-todo.tsx:52:88)
    at div
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-primitive/dist/index.mjs:44:26)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-dismissable-layer/dist/index.mjs:44:42)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-slot/dist/index.mjs:46:23)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-slot/dist/index.mjs:20:23)
    at eval (webpack-internal:///(app-pages-browser)/./node_modules/@radix-ui/react-primitive/dist/index.mjs:44:26)

这是我在表单中创建新类别时遇到的错误,我正在使用 zod 进行表单验证,但我无法判断错误来自哪里,它指向我从

input
安装的
shadcn-ui
组件但我看了看,没有发现任何问题,我什至没有碰它。

这是我的组件导致了问题

"use client";

import { toast } from "react-hot-toast";
import * as z from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { PencilLine, Plus } from "lucide-react";
import { Button } from "./ui/button";
import {
  SelectValue,
  SelectTrigger,
  SelectContent,
  SelectItem,
  Select,
} from "@/components/ui/select";
import { useEffect, useRef, useState } from "react";
import { createTodo, getCategories } from "@/app/todos/actions";
import { Category } from "@/types/custom";

const formSchema = z
  .object({
    title: z.string().min(3),
    date: z.string(),
    category: z.string(),
    newCategory: z.string().optional(),
  })
  .refine(
    (data) => {
      if (data.newCategory?.toLowerCase().split(" ").join("") === "createnew") {
        return false;
      } else if (data.category === "Create new" && !data.newCategory) {
        return false;
      }
      return true;
    },
    {
      message: "New category cannot be empty or named 'Create new'.",
      path: ["newCategory"],
    }
  );

function FormContent() {
  const [categories, setCategories] = useState<Category[] | null>();

  useEffect(() => {
    const fetchCategories = async () => {
      const fetched_categories = await getCategories();
      setCategories(fetched_categories);
    };

    fetchCategories();
  }, []);

  const formRef = useRef<HTMLFormElement>(null);

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      title: "",
      date: "",
    },
  });

  const category = form.watch("category");

  const handleSubmit = async (data: any) => {
    if (data.category === "Create new") {
      data.category = null;
    } else {
      data.newCategory = null;
    }
    await createTodo(data);
    formRef.current?.reset();
  };

  return (
    <Form {...form}>
      <form
        onSubmit={form.handleSubmit(handleSubmit)}
        ref={formRef}
        className="w-full flex flex-col gap-4"
      >
        <FormField
          control={form.control}
          name="title"
          render={({ field }) => {
            return (
              <FormItem>
                <FormLabel>Title</FormLabel>
                <FormControl>
                  <Input placeholder="Title" type="text" {...field} />
                </FormControl>
                <FormMessage />
              </FormItem>
            );
          }}
        />
        <FormField
          control={form.control}
          name="date"
          render={({ field }) => {
            return (
              <FormItem>
                <FormLabel>Date</FormLabel>
                <FormControl>
                  <Input placeholder="Date" type="date" {...field} />
                </FormControl>
                <FormMessage />
              </FormItem>
            );
          }}
        />
        <FormField
          control={form.control}
          name="category"
          render={({ field }) => {
            return (
              <FormItem>
                <FormLabel>Category</FormLabel>
                <Select
                  onValueChange={(value) => {
                    field.onChange(value); // Ensures value is passed to form control
                    form.setValue("category", value); // Sets value directly to the form
                  }}
                >
                  <FormControl>
                    <SelectTrigger>
                      <SelectValue placeholder="Select a category" />
                    </SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    {categories &&
                      categories.map((category: any) => (
                        <SelectItem key={category.id} value={category.name}>
                          {category.name}
                        </SelectItem>
                      ))}
                    <SelectItem value="Create new">Create new</SelectItem>
                  </SelectContent>
                </Select>
                <FormMessage />
              </FormItem>
            );
          }}
        />
        {category === "Create new" && (
          <FormField
            control={form.control}
            name="newCategory"
            render={({ field }) => {
              return (
                <FormItem>
                  <FormLabel>New category</FormLabel>
                  <FormControl>
                    <Input placeholder="New category" type="text" {...field} />
                  </FormControl>
                  <FormMessage />
                </FormItem>
              );
            }}
          />
        )}
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  );
}

export default function CreateTodoDialog() {
  return (
    <Dialog>
      <DialogTrigger className="flex gap-x-1 hover:cursor-pointer">
        <Plus className="text-blue-500" />
        Add a todo
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Create a new todo</DialogTitle>
        </DialogHeader>
        <FormContent />
      </DialogContent>
    </Dialog>
  );
}

这是我的

input
组件。

import * as React from "react";

import { cn } from "@/lib/utils";

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-slate-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-800 dark:bg-slate-950 dark:ring-offset-slate-950 dark:placeholder:text-slate-400 dark:focus-visible:ring-slate-300",
          className
        )}
        ref={ref}
        {...props}
      />
    );
  }
);
Input.displayName = "Input";

export { Input };

reactjs forms zod shadcnui
1个回答
0
投票

您必须定义

category
newCategory
的默认值。 发生这种情况是因为组件的值从未定义更改为已定义。

在你的

form
变量上:

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      title: "",
      date: "",
      newCategory: "", // Add default value HERE
      category: "", // Add default value HERE
    },
  });

来源:https://github.com/shadcn-ui/ui/issues/410

© www.soinside.com 2019 - 2024. All rights reserved.