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

React关联MongoDB双Schema填充下拉框报错的解决方法

问题解决:React下拉框关联MongoDB双Schema时的报错处理

问题背景

我在MongoDB中有两个Schema:

  • Assign Schema:包含user_story_id和user_id字段
  • User_story Schema:包含用户故事描述字段

需求是在React表单的下拉框中展示已分配的用户故事,需要关联两个Schema获取对应的ID与描述。以下是我的React实现代码,但运行时报错:

import React, { useState, useEffect } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import axios from 'axios';

export default function Task({}) {
  const [taskValues, setTaskValues] = useState({
    task: '',
    userStory: 'userStory1', // Default value
    createdBy: 'user1',       // Default value
    status: 'Pending',       // Default value
  });

  const [allTask, setAllTask] = useState([{}]);
  const [assigned, setAllAssign] = useState([{}]);
  const [allStories, setAllStories] = useState([{}]);

  const assignedUserStories = assigned.map((assigns) => {
    return assigns.user_story_id;
  })
  
  const handleChange = (event) => {
    setTaskValues({ ...taskValues, [event.target.name]: event.target.value });
  };

  const loggedInUser = localStorage.getItem('loggedInUser')
  const handleCreateTask = (e) => {
    e.preventDefault();
    const taskDetails = {
      task: taskValues.task,     //captured from input task
      user_story_id: taskValues.userStory,      //captured from dropdown
      status: taskValues.status,      //captured from status dropdown
      created_by: loggedInUser
    }

    // Post taskValues data to your server endpoint
    axios.post('http://localhost:9000/createTask', taskValues)
      .then((res) => {
        if (res.data) {
          alert('Task creation Successful');
        }
      })
      .catch((err) => {
        alert('Error in creating the task');
      });
  };

  useEffect(() => {
    axios.get('http://localhost:9000/getTaskList')
      .then(function (response) {
        const Taskdata = response.data;
        setAllTask(Taskdata);
      });

    axios.get('http://localhost:9000/getUserstory')
      .then(function (response) {
        const Allstories = response.data;
        setAllStories(Allstories);
      });
  }, []);
 
  const handleSubmit = (event) => {
    event.preventDefault();
  };

  return (
    <div className="container mt-5">
      <div className="row justify-content-center">
        <div className="col-md-6 col-lg-4">
          <div className="card">
            <div className="card-body">
              <h2 className="card-title">Create a New Task</h2>
              <form onSubmit={handleSubmit}>
                <div className="form-group">
                  <label>Task:</label>
                  <textarea
                    className="form-control"
                    name="task"
                    value={taskValues.task}
                    onChange={handleChange}
                  />
                </div>

                <div className="form-group">
                  <label>User Story:</label>
                  <select
                    className="form-control"
                    name="userStory"
                    value={taskValues.userStory}
                    onChange={handleChange}
                  >
                    {assignedUserStories.map((stories) =>
                      <option key={stories._id} value={stories._id}>{stories.user_story}</option>
                    
                    )}
                   
                  </select>
                </div>

                <div className="form-group">
                  <label>Created by:</label>
                  <select
                    className="form-control"
                    name="createdBy"
                    value={taskValues.createdBy}
                    onChange={handleChange}
                  >
                    <option value="user1">User 1</option>
                    <option value="user2">User 2</option>
                    <option value="user3">User 3</option>
                  </select>
                </div>

                <div className="form-group">
                  <label>Status:</label>
                  <select
                    className="form-control"
                    name="status"
                    value={taskValues.status}
                    onChange={handleChange}
                  >
                    <option value="New">New</option>
                    <option value="In Progress">In Progress</option>
                    <option value="Awaiting Confirmation">Awaiting Confirmation</option>
                    <option value="Completed">Completed</option>
                    
                  </select>
                </div>

                      <button
                      onClick={() => handleCreateTask()}
                      className="btn btn-primary btn-block"
                      >
                      Assign
                      </button>
              </form>
            </div>
          </div>
          
        </div>
      </div>
    </div>
  );
}

报错原因分析

  1. 未获取Assign数据:代码中仅请求了任务列表和用户故事列表,但没有调用接口获取Assign集合的数据,导致assigned状态始终是初始的[{}],map后得到的assignedUserStories包含undefined,渲染时访问stories._id会报错。
  2. 初始状态不合理:用[{}]作为数组初始值,第一次渲染时会处理空对象,引发属性读取错误。
  3. 数据关联逻辑错误:直接将user_story_id当作完整的用户故事对象使用,无法获取到描述字段。

解决步骤

1. 补充Assign数据接口请求

在useEffect中添加获取Assign列表的请求,拿到已分配的用户故事ID:

useEffect(() => {
  axios.get('http://localhost:9000/getTaskList')
    .then(res => setAllTask(res.data));

  axios.get('http://localhost:9000/getUserstory')
    .then(res => setAllStories(res.data));

  // 新增:获取Assign集合数据
  axios.get('http://localhost:9000/getAssignList')
    .then(res => setAllAssign(res.data));
}, []);

2. 修正初始状态

将数组初始值改为空数组,避免空对象干扰:

const [assigned, setAllAssign] = useState([]);
const [allStories, setAllStories] = useState([]);

3. 正确关联双Schema数据

通过Assign中的user_story_id,从User_story集合中筛选出已分配的故事:

// 提取去重的已分配故事ID
const assignedStoryIds = [...new Set(assigned.map(item => item.user_story_id))];
// 筛选出对应的用户故事
const assignedUserStories = allStories.filter(story => assignedStoryIds.includes(story._id));

4. 修正下拉框渲染逻辑

确保只在有数据时渲染选项,避免空数组报错:

<div className="form-group">
  <label>User Story:</label>
  <select
    className="form-control"
    name="userStory"
    value={taskValues.userStory}
    onChange={handleChange}
  >
    {assignedUserStories.length > 0 ? (
      assignedUserStories.map((story) => (
        <option key={story._id} value={story._id}>{story.user_story}</option>
      ))
    ) : (
      <option value="">暂无已分配的用户故事</option>
    )}
  </select>
</div>

5. 其他优化点

  • 状态命名优化:建议将assigned改为assigns,setAllAssign改为setAssigns,避免命名混淆。
  • 表单提交修正:handleCreateTask中应提交构造好的taskDetails而非taskValues:
    axios.post('http://localhost:9000/createTask', taskDetails)
    
  • 默认值修正:将taskValues.userStory的默认值改为空字符串,避免下拉框出现不存在的选项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:48:12