如何基于API返回的JSON响应在React中渲染动态HTML表格
需求说明
我需要遍历表格JSON的字段值生成如下结构的HTML表格:
<div> <table> <tr> <td>Rows</td> <td>List 1 </td> <td>List 2 </td> </tr> <tr> <td>Row 1</td> <td>task 1, task 2</td> <td>task 3</td> </tr> <tr> <td>Row 2</td> <td>task 4</td> <td>task 5, task 6</td> </tr> </table> </div>
高层逻辑是遍历JSON中的rows,示例JSON包含2行;因为table.lists有list1、list2两个值,每行对应生成2个额外列。行单元格的值来自JSON的table.data部分,该部分通过rowId、listId关联对应taskId。
现有React组件代码
import React, { useEffect, useState, useMemo } from 'react'; import { useAppSelector, useAppDispatch } from '../../app/hooks'; import { selectTable, fetchTableAsync, } from './tableSlice'; import { DragDropContext, Draggable, Droppable, DroppableProvided, DraggableLocation, DropResult, DroppableStateSnapshot, DraggableProvided, DraggableStateSnapshot } from 'react-beautiful-dnd'; export function Table() { const table = useAppSelector(selectTable); const dispatch = useAppDispatch(); useEffect(() => { dispatch(fetchTableAsync("")) }, []); console.log('select table value is: '); console.log(JSON.stringify(table)); return ( <div> <table> <tr> <td>Rows</td> <td>List 1 </td> <td>List 2 </td> </tr> <tr> <td>Row 1</td> <td>task 1, task 2</td> <td>task 3</td> </tr> <tr> <td>Row 2</td> <td>task 4</td> <td>task 5, task 6</td> </tr> </table> </div> ); }
API返回JSON结构
{ "table": { "id": "aecf78db-e893-40e7-8a5a-e4150cc5129c", "data": { "row1": { "list1": [ { "task1": { "sort": 1 } }, { "task2": { "sort": 2 } } ], "list2": [ { "task3": { "sort": 1 } } ] }, "row2": { "list1": [ { "task4": { "sort": 1 } } ], "list2": [ { "task5": { "sort": 1 } }, { "task6": { "sort": 1 } } ] } } }, "lists": [ { "list1": { "name": "List 1", "sort": 1 } }, { "list2": { "name": "List 2", "sort": 2 } } ], "rows": { "row1": { "title": "Row 1" }, "row2": { "title": "Row 2" } }, "tasks": { "task1": { "title": "task 1" }, "task2": { "title": "task 2" }, "task3": { "title": "task 3" }, "task4": { "title": "task 4" }, "task5": { "title": "task 5" }, "task6": { "title": "task 6" } } }
实现代码
直接复用你设计的键值对查询结构,所有关联查找都是O(1)复杂度,保证大数据量下的渲染效率:
export function Table() { const table = useAppSelector(selectTable); const dispatch = useAppDispatch(); useEffect(() => { dispatch(fetchTableAsync("")) }, []); // 提前计算排序后的列表,避免渲染时重复计算 const sortedLists = useMemo(() => { if (!table?.lists) return [] return table.lists.sort((a, b) => { const aKey = Object.keys(a)[0] const bKey = Object.keys(b)[0] return a[aKey].sort - b[bKey].sort }) }, [table?.lists]) // 数据未加载完成返回占位 if (!table || !table.rows || !sortedLists.length) return <div>加载中...</div> return ( <div> <table> {/* 表头渲染 */} <tr> <td>Rows</td> {sortedLists.map(listItem => { const listId = Object.keys(listItem)[0] return <td key={listId}>{listItem[listId].name}</td> })} </tr> {/* 逐行渲染 */} {Object.entries(table.rows).map(([rowId, rowInfo]) => ( <tr key={rowId}> <td>{rowInfo.title}</td> {/* 每行渲染所有列表对应的列 */} {sortedLists.map(listItem => { const listId = Object.keys(listItem)[0] // 直接通过键取对应行对应列的任务列表 const taskList = table.table.data?.[rowId]?.[listId] || [] // 按sort排序后拼接任务名称 const taskNames = taskList .sort((a, b) => { const aTaskId = Object.keys(a)[0] const bTaskId = Object.keys(b)[0] return a[aTaskId].sort - b[bTaskId].sort }) .map(taskItem => { const taskId = Object.keys(taskItem)[0] return table.tasks[taskId]?.title || '' }) .filter(Boolean) .join(', ') return <td key={`${rowId}-${listId}`}>{taskNames}</td> })} </tr> ))} </table> </div> ); }
后续如果要接入拖拽功能,只需要在对应单元格内嵌套Droppable、Draggable组件即可。
内容的提问来源于stack exchange,提问作者Blankman
相关产品推荐
相关产品推荐

