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

NextJS+MongoDB环境下员工集合关联到公司集合的实现方案咨询

实现方案

你的核心思路是可行的,结合react-hook-form的受控组件能力可以更简单实现,不需要额外手动维护选择状态,直接通过表单能力自动收集选中的员工ID,完美适配你定义的Mongoose关联字段要求,以下是完整修正代码:


步骤1:修改父组件(创建公司页面)

主要调整:从useForm中解构出control传递给子选择器,修正提交逻辑不要写死员工数组为空。

export default function CreateSet() {
  // 新增解构control用于自定义组件受控
  const { register, handleSubmit, control } = useForm();
  const router = useRouter();
  const { redirect } = router.query;
  const { state, dispatch } = useContext(Store);
  const { vendorInfo } = state;
  
  useEffect(() => {
    if (!vendorInfo) {
      router.push('/');
    }
  }, [vendorInfo]);

  const submitHandler = async ({ name, employees }) => {
    try {
      const { data } = await axios.post(
        '/api/company/',
        { name, employees }, // 直接传递表单收集的选中员工ID数组
        { headers: { authorization: `Bearer ${vendorInfo.token}` } }
      );
      dispatch({ type: 'COMPANY_CREATE', payload: data });
      router.push(redirect || '/vendor/');
    } catch (err) {
      console.log(getError(err));
    }
  };
  
  return (
    <div title='CompanyCreation' className={styles.register_container}>
      <form onSubmit={handleSubmit(submitHandler)}>
        <h1>Create Company</h1>
        <ul>
          <li>
            <input
              id='name'
              label='name'
              placeholder='Name'
              {...register('name', { required: true })}
            />
          </li>
          <li>
            <p>Employees:</p>
            {/* 把control传递给员工选择器组件 */}
            <EmployeeSelector control={control} />
          </li>
          <li>
            <button type='submit'>Create</button>
          </li>
        </ul>
      </form>
    </div>
  );
}

步骤2:修改员工选择器组件

主要调整:用react-hook-form的Controller包裹选择列表,自动管理选中状态,不需要手动写选择处理逻辑,同时修正原代码的语法错误、依赖缺失问题。

import React, { useEffect, useReducer, useContext } from 'react';
import axios from 'axios';
import Table from 'react-bootstrap/Table';
import dynamic from 'next/dynamic';
import { Controller } from 'react-hook-form'; // 导入受控组件包装器
import { getError } from '../utils/error';
import { Store } from '../utils/store';

function reducer(state, action) {
  switch (action.type) {
    case 'FETCH_REQUEST':
      return { ...state, loading: true, error: '' };
    case 'FETCH_SUCCESS':
      return { ...state, loading: false, employees: action.payload, error: '' };
    case 'FETCH_FAIL':
      return { ...state, loading: false, error: action.payload };
    default:
      return state; // 修正原代码漏return的问题
  }
}

// 接收父组件传的control参数
function EmployeeList({ control }) {
  const { state } = useContext(Store);
  const { vendorInfo } = state;

  // 修正原代码reducer返回字段错别字,删除无用的products变量
  const [{ loading, error, employees }, dispatch] = useReducer(reducer, {
    loading: true,
    employees: [],
    error: '',
  });

  useEffect(() => {
    const fetchData = async () => {
      try {
        dispatch({ type: 'FETCH_REQUEST' });
        const { data } = await axios.get(`/api/employee`, {
          headers: { authorization: `Bearer ${vendorInfo.token}` },
        });
        dispatch({ type: 'FETCH_SUCCESS', payload: data });
      } catch (err) {
        dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
      }
    };
    fetchData();
  }, [vendorInfo]); // 补充依赖避免渲染警告

  return (
    <div title='EmployeeList'>
      {loading ? (
        <p>Loading</p>
      ) : error ? (
        <p>{error}</p>
      ) : (
        // 用Controller把选择列表变成react-hook-form受控组件
        <Controller
          control={control}
          name="employees"
          rules={{ required: '请至少选择一名员工' }} // 可自定义校验规则
          render={({ field }) => (
            <Table striped bordered>
              <thead>
                <tr>
                  <th>Select</th>
                  <th>First name</th>
                  <th>Last name</th>
                </tr>
              </thead>
              <tbody>
                {employees.map((employee) => (
                  <tr key={employee._id}>
                    <td>
                      <input
                        type="checkbox"
                        value={employee._id} // 选中值直接绑定员工ID,适配后端字段要求
                        checked={field.value?.includes(employee._id)}
                        onChange={(e) => {
                          const selected = [...field.value || []];
                          e.target.checked 
                            ? selected.push(employee._id) 
                            : selected.splice(selected.indexOf(employee._id), 1);
                          field.onChange(selected);
                        }}
                      />
                    </td>
                    <td>{employee.firstName}</td>
                    <td>{employee.lastName}</td>
                  </tr>
                ))}
              </tbody>
            </Table>
          )}
        />
      )}
    </div>
  );
}

export default dynamic(() => Promise.resolve(EmployeeList), { ssr: false });

逻辑说明

通过Controller包装后,选中的员工ID会自动存入表单的employees字段,提交时直接传给后端,正好匹配你companySchema中定义的关联ID数组格式,不需要额外做数据转换,也不需要手动维护选择状态。如果需要限制最少/最多选择数量,直接在Controller的rules里添加对应校验规则即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 09:36:07