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
相关产品推荐
相关产品推荐

