React-hook-forms ForwardRef 警告与 shadcn/ui 元素

问题描述 投票:0回答:1

您好,我正在尝试使用

react-hook-form
shadcn 组合框 制作表单。有2个文件。

  1. 类别形式.tsx
  2. combobox.tsx (在category-form.tsx中使用)
  • 类别形式.tsx
'use client';

import * as z from 'zod';
import axios from 'axios';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { Pencil } from 'lucide-react';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { useRouter } from 'next/navigation';
import { Course } from '@prisma/client';

import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { Combobox } from '@/components/ui/combobox';

interface CategoryFormProps {
    initialData: Course;
    courseId: string;
    options: { label: string; value: string }[];
}

const formSchema = z.object({
    categoryId: z.string().min(1),
});

export const CategoryForm = ({ initialData, courseId, options }: CategoryFormProps) => {
    const [isEditing, setIsEditing] = useState(false);

    const toggleEdit = () => setIsEditing((current) => !current);

    const router = useRouter();

    const form = useForm<z.infer<typeof formSchema>>({
        resolver: zodResolver(formSchema),
        defaultValues: {
            categoryId: initialData?.categoryId || '',
        },
    });

    const { isSubmitting, isValid } = form.formState;

    const onSubmit = async (values: z.infer<typeof formSchema>) => {
        try {
            await axios.patch(`/api/courses/${courseId}`, values);
            toast.success('Course updated');
            toggleEdit();
            router.refresh();
        } catch {
            toast.error('Something went wrong');
        }
    };

    const selectedOption = options.find((option) => option.value === initialData.categoryId);

    return (
        <div className='mt-6 rounded-md border bg-slate-100 p-4'>
            <div className='flex items-center justify-between font-medium'>
                Course category
                <Button onClick={toggleEdit} variant='ghost'>
                    {isEditing ? (
                        <>Cancel</>
                    ) : (
                        <>
                            <Pencil className='mr-2 h-4 w-4' />
                            Edit category
                        </>
                    )}
                </Button>
            </div>
            {!isEditing && (
                <p
                    className={cn(
                        'mt-2 text-sm',
                        !initialData.categoryId && 'italic text-slate-500'
                    )}
                >
                    {selectedOption?.label || 'No category'}
                </p>
            )}
            {isEditing && (
                <Form {...form}>
                    <form onSubmit={form.handleSubmit(onSubmit)} className='mt-4 space-y-4'>
                        <FormField
                            control={form.control}
                            name='categoryId'
                            render={({ field }) => {
                                return (
                                    <FormItem>
                                        <FormControl>
                                            <Combobox options={...options} {...field} />
                                        </FormControl>
                                        <FormMessage />
                                    </FormItem>
                                );
                            }}
                        />
                        <div className='flex items-center gap-x-2'>
                            <Button disabled={!isValid || isSubmitting} type='submit'>
                                Save
                            </Button>
                        </div>
                    </form>
                </Form>
            )}
        </div>
    );
};
"use client"

import * as React from "react"
import { Check, ChevronsUpDown } from "lucide-react"

import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
} from "@/components/ui/command"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"

interface ComboboxProps {
  options: { label: string; value: string }[];
  value?: string;
  onChange: (value: string) => void;
};

export const Combobox = ({
  options,
  value,
  onChange
}: ComboboxProps) => {
  const [open, setOpen] = React.useState(false)

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          className="w-full justify-between"
        >
          {value
            ? options.find((option) => option.value === value)?.label
            : "Select option..."}
          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-full p-0">
        <Command>
          <CommandInput placeholder="Search option..." />
          <CommandEmpty>No option found.</CommandEmpty>
          <CommandGroup>
            {options.map((option) => (
              <CommandItem
                key={option.value}
                onSelect={() => {
                  onChange(option.value === value ? "" : option.value)
                  setOpen(false)
                }}
              >
                <Check
                  className={cn(
                    "mr-2 h-4 w-4",
                    value === option.value ? "opacity-100" : "opacity-0"
                  )}
                />
                {option.label}
              </CommandItem>
            ))}
          </CommandGroup>
        </Command>
      </PopoverContent>
    </Popover>
  )
}
  • 当我使用该应用程序时,我收到一条错误消息:
    Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()

我相信会发生,从

category-form.tsx
内的这一行 <Combobox options={...options} {...field} /> 开始。当我展开字段时,引用也会传递到组合框。

为了处理这个问题,我需要将

Combobox
包裹在
React.forwardRef
周围,但我不确定 comboform.tsx 中的哪个元素与
ref
相关联。我想我应该是
Button
(在comboform.tsx中),所以这就是我所做的->


import * as React from 'react';
import { Check, ChevronsUpDown } from 'lucide-react';

import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import {
    Command,
    CommandEmpty,
    CommandGroup,
    CommandInput,
    CommandItem,
} from '@/components/ui/command';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';

interface ComboboxProps {
    options: { label: string; value: string }[];
    value?: string;
    onChange: (value: string) => void;
}

const Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(
    ({ options, value, onChange }: ComboboxProps, ref) => {
        const [open, setOpen] = React.useState(false);

        return (
            <Popover open={open} onOpenChange={setOpen}>
                <PopoverTrigger asChild>
                    <Button
                        variant='outline'
                        role='combobox'
                        aria-expanded={open}
                        className='w-full justify-between'
                        ref={ref}          // THIS IS WHERE I AM ASSOCIATING MY REF
                    >
                        {value
                            ? options.find((option) => option.value === value)?.label
                            : 'Select option...'}
                        <ChevronsUpDown className='ml-2 h-4 w-4 shrink-0 opacity-50' />
                    </Button>
                </PopoverTrigger>
                <PopoverContent className='w-full p-0'>
                    <Command>
                        <CommandInput placeholder='Search option...' />
                        <CommandEmpty>No option found.</CommandEmpty>
                        <CommandGroup>
                            {options.map((option) => (
                                <CommandItem
                                    key={option.value}
                                    onSelect={() => {
                                        onChange(option.value === value ? '' : option.value);
                                        setOpen(false);
                                    }}
                                >
                                    <Check
                                        className={cn(
                                            'mr-2 h-4 w-4',
                                            value === option.value ? 'opacity-100' : 'opacity-0'
                                        )}
                                    />
                                    {option.label}
                                </CommandItem>
                            ))}
                        </CommandGroup>
                    </Command>
                </PopoverContent>
            </Popover>
        );
    }
);

Combobox.displayName = 'Combobox';

export { Combobox };

  • 请告诉我我是否正确,或者我应该将我的
    ref
    与其他一些元素相关联,在这种情况下,请以打字稿不会对我大喊大叫的方式告诉我。 谢谢你。
reactjs typescript next.js react-hook-form radix-ui
1个回答
0
投票

尝试在没有扩展运算符的情况下传递选项。

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