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

React组件编辑页面下拉框无法显示指定部门名的问题求助

问题

使用MongoDB作为数据库,存在一个名为departments的集合,包含name、description属性。开发的React组件JobDescriptionEdit.jsx在编辑职位描述时,下拉框无法显示创建该职位时选择的对应部门名(当前下拉框展示所有部门名),且控制台提示department为undefined,需要解决下拉框显示指定部门名的问题。

import axios from "axios";
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

const JobDescriptionEdit = () => {
  const { id } = useParams(); // Get the job description ID from the URL
  const [positionId, setPositionId] = useState("");
  const [departmentId, setDepartmentId] = useState("");
  const [jobDescription, setJobDescription] = useState("");
  const [isApproved, setIsApproved] = useState(false);
  const [departments, setDepartments] = useState([]);
  const [positions, setPositions] = useState([]);
  const [loading, setLoading] = useState(true); // Loading state
  const navigate = useNavigate();

  useEffect(() => {
    // Fetch departments and positions data
    const fetchDepartmentsAndPositions = async () => {
      try {
        const token = localStorage.getItem("token");
        if (!token) {
          toast.error("Please login to access department and position data.");
          return;
        }

        const [deptResponse, posResponse] = await Promise.all([
          axios.get("http://localhost:3000/api/v1/departments", {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }),
          axios.get("http://localhost:3000/api/v1/positions", {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }),
        ]);

        console.log("Departments data:", deptResponse.data.departments);
        console.log("Positions data:", posResponse.data.positions);

        setDepartments(deptResponse.data.departments);
        setPositions(posResponse.data.positions);
      } catch (error) {
        console.error("Error fetching departments and positions data:", error);
        toast.error("Error fetching departments and positions data");
      } finally {
        setLoading(false); // Set loading to false after data is fetched
      }
    };

    fetchDepartmentsAndPositions();
  }, []);

  useEffect(() => {
    // If in edit mode, fetch job description data and populate form fields
    if (id) {
      const fetchJobDescription = async () => {
        try {
          const token = localStorage.getItem("token");
          if (!token) {
            navigate("/login");
            return;
          }

          const response = await axios.get(
            `http://localhost:3000/api/v1/jobdescriptions/${id}`,
            {
              headers: {
                Authorization: `Bearer ${token}`,
              },
            }
          );

          const { positionId, departmentId, jobDescription, isApproved } =
            response.data.jobDescription;

          console.log("Fetched job description:", response.data.jobDescription);

          setPositionId(positionId);
          setDepartmentId(departmentId);
          setJobDescription(jobDescription);
          setIsApproved(isApproved);
        } catch (error) {
          console.error("Error fetching job description data:", error);
          toast.error("Error fetching job description data");
          if (error.response && error.response.status === 401) {
            navigate("/login");
          }
        }
      };
      fetchJobDescription();
    }
  }, [id, navigate]);

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const token = localStorage.getItem("token");
      if (!token) {
        navigate("/login");
        return;
      }

      const updateBy = null;
      const approvedBy = null;

      if (id) {
        // If in edit mode, update existing job description
        await axios.patch(
          `http://localhost:3000/api/v1/jobdescriptions/${id}`,
          {
            positionId,
            departmentId,
            jobDescription,
            isApproved,
            updateBy,
            approvedBy,
          },
          {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }
        );
        toast.success("Job description updated successfully");
      } else {
        // If in create mode, create new job description
        await axios.post(
          "http://localhost:3000/api/v1/jobdescriptions",
          {
            positionId,
            departmentId,
            jobDescription,
            isApproved,
            updateBy,
            approvedBy,
          },
          {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          }
        );
        toast.success("Job description created successfully");
      }
      navigate("/job-description"); // Redirect to job descriptions page after submission
    } catch (error) {
      console.error("Error submitting job description:", error);
      toast.error("Error submitting job description");
      if (error.response && error.response.status === 401) {
        navigate("/login");
      }
    }
  };

  if (loading) {
    return <div>Loading...</div>; // Render loading message while fetching data
  }

  return (
    <div className="min-h-screen flex flex-col">
      <div className="max-w-lg px-10 w-full">
        <h1 className="text-2xl font-bold my-8">
          {id ? "Edit Job Description" : "Create Job Description"}
        </h1>
        <form onSubmit={handleSubmit}>
          <div className="mb-4">
            <label
              className="block text-sm font-medium mb-2"
              htmlFor="departmentId"
            >
              Department
            </label>
            <select
              id="departmentId"
              value={departmentId}
              onChange={(e) => setDepartmentId(e.target.value)}
              className="select select-bordered w-full"
              required
            >
              <option value="">Select a department</option>
              {departments.map((dept) => (
                <option key={dept._id} value={dept._id}>
                  {dept.name}
                </option>
              ))}
            </select>
          </div>
          <div className="mb-4">
            <label
              className="block text-sm font-medium mb-2"
              htmlFor="positionId"
            >
              Position
            </label>
            <select
              id="positionId"
              value={positionId}
              onChange={(e) => setPositionId(e.target.value)}
              className="select select-bordered w-full"
              required
            >
              <option value="">Select a position</option>
              {positions.map((pos) => (
                <option key={pos._id} value={pos._id}>
                  {pos.name}
                </option>
              ))}
            </select>
          </div>

          <div className="mb-4">
            <label
              className="block text-sm font-medium mb-2"
              htmlFor="jobDescription"
            >
              Job Description
            </label>
            <textarea
              id="jobDescription"
              value={jobDescription}
              onChange={(e) => setJobDescription(e.target.value)}
              className="textarea textarea-bordered w-full"
              required
            ></textarea>
          </div>
          <div className="mb-4">
            <label className="block text-sm font-medium mb-2">
              <input
                type="checkbox"
                checked={isApproved}
                onChange={(e) => setIsApproved(e.target.checked)}
                className="checkbox"
              />
              Approved
            </label>
          </div>
          <div className="flex justify-between">
            <button
              type="button"
              className="btn btn-sm btn-secondary"
              onClick={() => navigate("/job-description")}
            >
              Cancel
            </button>
            <button type="submit" className="btn btn-sm btn-primary">
              {id ? "Update Job Description" : "Create Job Description"}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
};

export default JobDescriptionEdit;
解决方案

1. 排查后端返回的数据结构

控制台打印response.data.jobDescription,确认数据结构是否符合预期:

  • 如果后端返回的是关联的department对象(例如{ department: { _id: "xxx", name: "xxx" }, ... }),而非单独的departmentId字段,需要修改解构逻辑:
    // 替换原有解构代码
    const { positionId, department, jobDescription, isApproved } = response.data.jobDescription;
    // 用可选链避免department为undefined时报错
    setDepartmentId(department?._id || "");
    
  • 如果后端确实返回departmentId但值为undefined,需要检查后端接口的查询逻辑,确保正确关联部门ID。

2. 调整数据加载顺序

当前两个useEffect并行执行,可能职位详情先获取完成,但部门数据还未加载,导致下拉框无法匹配选中项。修改第二个useEffect,等待部门数据加载完成后再获取职位详情:

useEffect(() => {
  // 确保id存在且部门数据已加载完成
  if (id && departments.length > 0) {
    const fetchJobDescription = async () => {
      // ... 原有代码逻辑不变
    };
    fetchJobDescription();
  }
}, [id, navigate, departments]); // 添加departments到依赖数组

3. 处理数据类型不匹配问题

MongoDB的_id为字符串类型,若后端返回的departmentId是ObjectId对象,会导致下拉框的value(字符串)无法匹配。设置departmentId时转为字符串:

setDepartmentId(String(departmentId));

4. 调试确认部门ID匹配

在设置departmentId后添加调试代码,确认值是否有效:

console.log("Set departmentId:", departmentId);
console.log("Matching department:", departments.find(dept => dept._id === departmentId));

如果find结果为undefined,说明部门列表中没有对应ID的数据,需要检查后端部门接口是否返回了所有必要部门,或职位的departmentId是否有效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:45:54