如何基于Rails后端在React中实现嵌套表格动态展示及自动追加用户?
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
statusLabelsobject converts numeric status values to readable text (adjust labels to match your needs). - Dynamic User Addition: The
handleAddUserfunction sends a POST request to your Rails API, then appends the new user to theusersstate—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

