You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js14+Supabase下拉框值无法传递至Server Action问题

解决Next.js 14中自定义下拉框值无法传递到Server Action的问题

问题原因

你使用的Headless UI Listbox是纯UI交互组件,本身不会自动生成表单提交所需的表单控件(如<input>或<select>),因此选中的barangay值不会被包含在表单提交的FormData中,导致Server Action无法接收该字段。

解决方案

在自定义DropdownList组件内部添加一个隐藏的input元素,将选中的值绑定到该input的value属性,并设置对应的name属性,这样表单提交时就会自动携带该字段的值。

修改后的DropdownList.tsx代码

import React, { Fragment, useState, useEffect } from 'react';
import { Listbox, Transition } from '@headlessui/react';
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid';

type Props = {
  options: string[];
  selected: string;
  onSelect: (value: string) => void;
  required: boolean;
  placeholder: string;
  value: string;
  name: string;
};

function classNames(...classes: string[]) {
  return classes.filter(Boolean).join(' ');
}

const DropdownList: React.FC<Props> = ({ options, selected, onSelect, required, placeholder, value, name }) => {
  const [currentSelected, setCurrentSelected] = useState<string | null>(selected);

  useEffect(() => {
    setCurrentSelected(selected);
  }, [selected]);

  return (
    <Listbox value={currentSelected} onChange={(value) => { 
        if (value !== null) {
          setCurrentSelected(value); 
          onSelect(value);
        }
      }}>
      {({ open }) => (
        <>
          <Listbox.Label className="block text-sm font-medium leading-6 text-gray-900">
            Select an option
          </Listbox.Label>
          {/* 添加隐藏input,用于表单提交 */}
          <input 
            type="hidden" 
            name={name} 
            value={currentSelected || ''} 
            required={required}
          />
          <div className="relative mt-2">
            <Listbox.Button className={`relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus-ring-indigo-500 sm:text-sm sm:leading-6 ${required ? 'required' : ''}`}>
              <span className="ml-3 block truncate">{currentSelected || placeholder}</span>
              <span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
                <ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
              </span>
            </Listbox.Button>

            <Transition
              show={open}
              as={Fragment}
              leave="transition ease-in duration-100"
              leaveFrom="opacity-100"
              leaveTo="opacity-0"
            >
              <Listbox.Options className="absolute z-10 mt-1 max-h-56 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
                <Listbox.Option key="placeholder" value="" disabled>
                  {({ active }) => (
                    <span className={classNames('text-gray-500', 'ml-3 block truncate')}>
                      {placeholder}
                    </span>
                  )}
                </Listbox.Option>

                {options.map((option, i) => (
                  <Listbox.Option
                    key={i}
                    className={({ active }) =>
                      classNames(
                        active ? 'bg-indigo-600 text-white' : 'text-gray-900',
                        'relative cursor-default select-none py-2 pl-3 pr-9'
                      )
                    }
                    value={option}
                  >
                    {({ selected, active }) => (
                      <>
                        <span className={classNames(selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate')}>
                          {option}
                        </span>

                        {selected ? (
                          <span
                            className={classNames(
                              active ? 'text-white' : 'text-indigo-600',
                              'absolute inset-y-0 right-0 flex items-center pr-4'
                            )}
                          >
                            <CheckIcon className="h-5 w-5" aria-hidden="true" />
                          </span>
                        ) : null}
                      </>
                    )}
                  </Listbox.Option>
                ))}
              </Listbox.Options>
            </Transition>
          </div>
        </>
      )}
    </Listbox>
  );
};

export default DropdownList;

关键修改点

  • 在Listbox内部添加了<input type="hidden" />元素,将name设置为组件接收的props.name(即"barangay"),value绑定到当前选中的值currentSelected。
  • 同步设置了required属性,确保表单验证生效。

验证修改效果

修改后重新提交表单,Server Action中的FormData应该会包含barangay字段及其选中的值,此时你可以通过formData.get('barangay')获取该值并插入到Supabase数据库中。

内容的提问来源于stack exchange,提问作者JS3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:14:55