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

如何基于Rails后端在React中实现嵌套表格动态展示及自动追加用户?

Solution: Nested Table for Users & Todos (React + Rails)

First, let's tweak your Rails backend to return data in a nested structure that's easier for React to work with—your current query flattens user and todo data, which makes grouping todos by developer tricky. Then we'll build the React component to render the nested table and handle dynamic user additions.

Step 1: Optimize Rails Backend Response

Instead of returning flattened User objects with todo attributes, return each user with their associated todos as a nested array. This way, React can directly group todos by their developer.

Update your controller action to generate this structure:

# In your UsersController (or dedicated API controller)
def users_with_todos
  users = User.includes(:todos) # Eager load todos to avoid N+1 queries
  render json: users.as_json(
    only: [:id],
    include: {
      todos: { only: [:title, :status] }
    }
  )
end

This will return JSON like:

[
  {"id":2,"todos":[{"title":"Design home Page","status":1}]},
  {"id":4,"todos":[{"title":"Display developers","status":2},{"title":"Projects display to admin","status":2}]}
]

This nested format is far easier to work with for grouping todos under their respective developers.

Step 2: React Frontend Implementation

We'll create a component that fetches the nested data, renders a nested table using rowSpan to group todos under each developer, and handles adding new users dynamically.

Full Component Code

import { useState, useEffect, Fragment } from 'react';

const DeveloperTodoTable = () => {
  const [users, setUsers] = useState([]);
  const [newUserId, setNewUserId] = useState(''); // Example state for new user form

  // Fetch initial data on component mount
  useEffect(() => {
    fetch('/api/users_with_todos') // Update this to match your Rails API endpoint
      .then(res => res.json())
      .then(data => setUsers(data))
      .catch(err => console.error('Failed to fetch data:', err));
  }, []);

  // Handle adding a new user (example implementation)
  const handleAddUser = async (e) => {
    e.preventDefault();
    if (!newUserId) return;

    // Send POST request to Rails backend to create user
    const res = await fetch('/api/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content // Include Rails CSRF token
      },
      body: JSON.stringify({ user: { id: newUserId } }) // Adjust payload to match your User model params
    });

    if (res.ok) {
      const newUser = await res.json();
      // Append new user to the end of the list
      setUsers(prevUsers => [...prevUsers, newUser]);
      setNewUserId(''); // Clear form input
    }
  };

  // Map status numbers to human-readable labels
  const statusLabels = {
    1: 'Todo',
    2: 'Completed'
  };

  return (
    <div className="todo-table-container">
      {/* New User Form */}
      <form onSubmit={handleAddUser} style={{ marginBottom: '1rem' }}>
        <input
          type="text"
          placeholder="Enter new developer ID"
          value={newUserId}
          onChange={(e) => setNewUserId(e.target.value)}
        />
        <button type="submit">Add Developer</button>
      </form>

      {/* Nested Table */}
      <table border="1" cellPadding="8">
        <thead>
          <tr>
            <th>Developer ID</th>
            <th>Todo Title</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {users.map(user => (
            <Fragment key={user.id}>
              {/* Main row for the developer (spans all their todo rows) */}
              <tr>
                <td rowSpan={user.todos.length || 1}>{user.id}</td>
                {user.todos.length > 0 ? (
                  <>
                    <td>{user.todos[0].title}</td>
                    <td>{statusLabels[user.todos[0].status]}</td>
                  </>
                ) : (
                  <>
                    <td>No todos assigned</td>
                    <td>-</td>
                  </>
                )}
              </tr>
              {/* Additional rows for remaining todos (no developer ID cell) */}
              {user.todos.slice(1).map((todo, idx) => (
                <tr key={`${user.id}-todo-${idx}`}>
                  <td>{todo.title}</td>
                  <td>{statusLabels[todo.status]}</td>
                </tr>
              ))}
            </Fragment>
          ))}
        </tbody>
      </table>
    </div>
  );
};

export default DeveloperTodoTable;

Key Details Explained:

  • Nested Data Handling: The Rails backend returns each user with their todos array, so we can directly map over users and their todos in React.
  • Row Spanning: We use rowSpan={user.todos.length || 1} to make the Developer ID cell span all rows for that user's todos, creating the nested visual effect.
  • Status Mapping: The statusLabels object converts numeric status values to readable text (adjust labels to match your needs).
  • Dynamic User Addition: The handleAddUser function sends a POST request to your Rails API, then appends the new user to the users state—this automatically renders the new user at the end of the table.
  • Edge Case Handling: We account for users with no todos by showing a "No todos assigned" message.

Step 3: Rails Setup for New User Creation

Make sure your Rails UsersController has a create action that returns the new user with an empty todos array (since new users won't have todos initially):

def create
  @user = User.new(user_params)
  if @user.save
    render json: @user.as_json(only: [:id], include: { todos: { only: [:title, :status] } }), status: :created
  else
    render json: @user.errors, status: :unprocessable_entity
  end
end

private
def user_params
  params.require(:user).permit(:id) # Add other permitted attributes as needed
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:27:13