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

React表单选择Office后设备下拉无选项问题求助

问题:React表单Office与Equipment Name联动失效

需求逻辑:

  • 选择「Building Management Office」时,Equipment Name下拉框显示Spalding Basketball、Mikasa Volleyball;
  • 选择「Information Technology and Resource Office」时,显示Nikon D300、Profoto B10 Plus Strobe Lights;
  • 选择「Engineering and Science Laboratory Office」时,显示Pyrex Beaker、Pyrex Graduated Cycliner。

实际问题:选择对应Office后,设备下拉框无选项显示,尝试通过selectedItems变量匹配对应设备列表但未生效,相关代码如下:

import React, { useEffect } from "react";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { useForm, Controller } from "react-hook-form";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import Select from "react-select";
import Button from "@mui/material/Button";
import InnerContainer from "./InnerContainer";

const sample_bmo_item = [
  {
    label: "Spalding Basketball",
    value: "101",
  },
  {
    label: "Mikasa Volleyball",
    value: "102",
  },
];

const sample_itro_item = [
  {
    label: "Nikon D300",
    value: "201",
  },
  {
    label: "Profoto B10 Plus Strobe Lights",
    value: "202",
  },
];

const sample_eslo_item = [
  {
    label: "Pyrex Beaker",
    value: "301",
  },
  {
    label: "Pyrex Graduated Cycliner",
    value: "302",
  },
];

const offices = [
  { label: "Building Management Office", value: "Building Management Office" },
  {
    label: "Information Technology and Resource Office",
    value: "Information Technology and Resource Office",
  },
  {
    label: "Engineering and Science Laboratory Office",
    value: "Engineering and Science Laboratory Office",
  },
];

const purposes = [
  {
    label: "Academic Purposes - Assignment & Activities",
    value: "Academic Purposes - Assignment & Activities",
  },
  { label: "Organization Purposes", value: "Organization Purposes" },
  {
    label: "Academic Purposes - Midterms & Finals",
    value: "Academic Purposes - Midterms & Finals",
  },
  { label: "Special Events", value: "Special Events" },
  { label: "Others", value: "Others" },
];

const endorser = [
  {
    label: "Ms. -",
    value: "Ms. -",
  },
  {
    label: "Sir. -",
    value: "Sir. -",
  },
  {
    label: "Ms. -",
    value: "Ms. -",
  },
  {
    label: "Ms. -",
    value: "Ms. -",
  },
  {
    label: "Ms. -",
    value: "Ms. -",
  },
];

const BorrowForm = () => {
  const { control, handleSubmit, setValue, watch } = useForm();
  const selectedPurpose = watch("purposes");
  const selectedOffice = watch("offices");

  const selectedItems =
    selectedOffice === "Building Management Office"
      ? sample_bmo_item
      : selectedOffice === "Information Technology and Resource Office"
      ? sample_itro_item
      : selectedOffice === "Engineering and Science Laboratory Office"
      ? sample_eslo_item
      : [];

  const onSubmit = (data) => {
    console.log("submit hit with data:", data);
  };

  useEffect(() => {
    console.log("selected purpose changed to:", selectedPurpose);
  }, [selectedPurpose]);

  return (
    <InnerContainer>
    <LocalizationProvider dateAdapter={AdapterDayjs}>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="borrowing-transaction">
          <Typography>Borrowing Information</Typography>
          <div className="endorser">
            <Typography>Endorsed By</Typography>
            <Controller
              name="endorser"
              control={control}
              render={({ field }) => <Select {...field} options={endorser} />}
            />
          </div>
          <div className="office">
            <Typography>Office</Typography>
            <Controller
              name="offices"
              control={control}
              defaultValue=""
              render={({ field }) => <Select {...field} options={offices} />}
            />
          </div>

          <div className="purposes">
            <Typography>Purpose</Typography>
            <Controller
              name="purposes"
              control={control}
              render={({ field }) => (
                <Select
                  options={purposes}
                  {...field}
                  value={purposes.find((c) => c.value === field.value?.value)}
                />
              )}
            />
          </div>

          <div className="specific-purpose">
            <Typography>Specific Purpose</Typography>
            <TextField
              label="Enter Specific Purpose"
              variant="filled"
              fullWidth
              style={{
                backgroundColor:
                  selectedPurpose?.value === "Others" ? "white" : "yellow",
              }}
              disabled={selectedPurpose?.value === "Others" ? false : true}
              onChange={(e) =>
                setValue("specificPurposeInputText", e.target.value)
              }
            />
          </div>

          <div className="date-pickers">
            <Typography>Starting Date</Typography>
            <Controller
              control={control}
              name="start-date"
              render={({ field }) => (
                <DatePicker
                  placeholderText="Select date"
                  onChange={(date) => field.onChange(date)}
                  selected={field.value}
                />
              )}
            />

            <Typography>Returning Date</Typography>
            <Controller
              control={control}
              name="return-date"
              render={({ field }) => (
                <DatePicker
                  placeholderText="Select date"
                  onChange={(date) => field.onChange(date)}
                  selected={field.value}
                />
              )}
            />
          </div>
        </div>

        <div className="items">
          <Typography>Item</Typography>
          <div className="oneby">
            <div className="equipment-name">
              <Typography>Equipment Name</Typography>
              <Controller
                control={control}
                name="equipmentName"
                render={({ field }) => (
                  <Select
                    {...field}
                    options={selectedItems}
                  />
                )}
              />
            </div>

            <div className="start-date-item">
              <Typography>Starting Date</Typography>
              <Controller
                control={control}
                name="return-date-item"
                render={({ field }) => (
                  <DatePicker
                    placeholderText="Select date"
                    onChange={(date) => field.onChange(date)}
                    selected={field.value}
                  />
                )}
              />
            </div>
            <div className="return-date-item">
              <Typography>Returning Date</Typography>
              <Controller
                control={control}
                name="return-date"
                render={({ field }) => (
                  <DatePicker
                    placeholderText="Select date"
                    onChange={(date) => field.onChange(date)}
                    selected={field.value}
                  />
                )}
              />
            </div>
            <div className="quantity"></div>
            <Typography>Quantity</Typography>
            <TextField
              id="outlined-number"
              label="Number"
              type="number"
              InputLabelProps={{
                shrink: true,
              }}
            />
          </div>
        </div>

        <Button type="submit" variant="contained" color="primary">
          Submit
        </Button>
      </form>
    </LocalizationProvider>
    </InnerContainer>
  );
};

export default BorrowForm;

问题排查与解决方案

核心问题

watch("offices")获取到的是整个选项对象(比如{ label: "Building Management Office", value: "Building Management Office" }),而非字符串类型的value值。原代码中直接用selectedOffice === "Building Management Office"进行判断,条件永远不成立,导致selectedItems始终为空数组[]。

修复步骤

  1. 修正selectedItems的判断逻辑:
    将判断条件改为使用selectedOffice?.value匹配字符串:
const selectedItems =
  selectedOffice?.value === "Building Management Office"
    ? sample_bmo_item
    : selectedOffice?.value === "Information Technology and Resource Office"
    ? sample_itro_item
    : selectedOffice?.value === "Engineering and Science Laboratory Office"
    ? sample_eslo_item
    : [];
  1. 可选:重置Equipment Name选中值
    切换Office时,清空之前选中的设备值,避免残留无效选项,添加useEffect:
useEffect(() => {
  setValue("equipmentName", null);
}, [selectedOffice, setValue]);
  1. 可选:完善Office的默认值
    原代码中Office的Controller设置了defaultValue="",但react-select的默认值应为对应选项对象或null,修改为:
<Controller
  name="offices"
  control={control}
  defaultValue={null} // 改为null,匹配react-select的默认值规范
  render={({ field }) => <Select {...field} options={offices} />}
/>

修复后效果

选择对应Office后,Equipment Name下拉框会自动加载对应设备列表,切换Office时设备选择框会清空之前的选中值,符合预期联动逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:32:31