React从API获取JSON数据赋值常量渲染树形组件的实现方法
React 树形组件异步加载API数据实现方案
问题场景
开发React应用构建文件夹树形结构时,将JSON数据硬编码为treeData常量时功能运行正常,对应代码如下:
Treelist Page 代码
const treeData = [{here is my json}]; const TreeList = () => { return ( <> <Header title="Tree Data Visualization" /> <ExternalInfo page="treeList" /> <div className="row"> <div className="col text-center"> <div className="row mt-3 d-flex justify-content-center"> <div className="col-lg-8 text-left text-dark"> <Tree data = {treeData}/> </div> </div> </div> </div> </> ); }; export default TreeList;
Treelist Component 代码
const Tree = ({ data = [] }) => { return ( <div className="d-tree"> <ul className="d-flex d-tree-container flex-column"> {data.map((tree) => ( <TreeNode node={tree} /> ))} </ul> </div> ); }; const TreeNode = ({ node }) => { const [childVisible, setChildVisiblity] = useState(false); const hasChild = node.elements ? true : false; const hasChildelements = node.elements ? true : false; const hasNodeDescription = node.description ? true : false; return ( <li className="d-tree-node border-0"> <div className="d-flex" onClick={(e) => setChildVisiblity((v) => !v)}> {hasChild && ( <div className={`d-inline d-tree-toggler ${ childVisible ? "active" : "" }`} > <FontAwesomeIcon icon="caret-right" /> </div> )} </li> ); }; export default Tree;
当改为从API接口获取JSON数据赋值时功能出现异常,测试用API地址为https://someapi.free.beeceptor.com/。
故障核心原因
- API请求是异步行为,首次组件渲染时数据还未返回,未设置初始空值会导致
data.map执行报错 - 多数接口返回的响应会有外层包裹(比如
{code: 200, data: [/*树节点数组*/]}结构),直接将整个响应对象传入组件,和硬编码时的纯数组结构不匹配 - 原有
TreeNode组件缺失子节点递归渲染逻辑,即使数据正确返回也无法展示多层级结构 - 未处理加载、请求错误的边界场景,异常发生时无兜底提示
正确实现代码
1. 修正TreeList页面,增加异步数据拉取逻辑
import { useState, useEffect } from 'react'; import Header from './Header'; import ExternalInfo from './ExternalInfo'; import Tree from './Tree'; const TreeList = () => { // 初始值设为空数组,避免首次渲染map报错 const [treeData, setTreeData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); useEffect(() => { const fetchTreeData = async () => { setLoading(true); setError(''); try { const res = await fetch('https://someapi.free.beeceptor.com/'); if (!res.ok) throw new Error('请求失败'); const result = await res.json(); // 根据接口实际返回结构取树数组,以下为兼容写法 setTreeData(Array.isArray(result) ? result : result.data || []); } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchTreeData(); }, []); return ( <> <Header title="Tree Data Visualization" /> <ExternalInfo page="treeList" /> <div className="row"> <div className="col text-center"> <div className="row mt-3 d-flex justify-content-center"> <div className="col-lg-8 text-left text-dark"> {loading && <div>数据加载中...</div>} {error && <div className="text-danger">加载失败:{error}</div>} {!loading && !error && <Tree data={treeData}/>} </div> </div> </div> </div> </> ); }; export default TreeList;
2. 补全Tree组件的递归渲染逻辑
import { useState } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCaretRight } from '@fortawesome/free-solid-svg-icons'; const Tree = ({ data = [] }) => { return ( <div className="d-tree"> <ul className="d-flex d-tree-container flex-column list-unstyled pl-0"> {data.map((tree, idx) => ( <TreeNode key={tree.id || idx} node={tree} /> ))} </ul> </div> ); }; const TreeNode = ({ node }) => { const [childVisible, setChildVisiblity] = useState(false); const hasChild = Array.isArray(node.elements) && node.elements.length > 0; const hasNodeDescription = !!node.description; return ( <li className="d-tree-node border-0 pl-3"> <div className="d-flex align-items-center" onClick={() => setChildVisiblity(v => !v)} style={{cursor: hasChild ? 'pointer' : 'default'}}> {hasChild && ( <div className={`d-inline d-tree-toggler mr-1 transition-transform ${ childVisible ? "active" : "" }`} style={{transform: childVisible ? 'rotate(90deg)' : 'rotate(0)'}} > <FontAwesomeIcon icon={faCaretRight} /> </div> )} {/* 节点名称,根据实际接口字段调整 */} <span className="mr-2">{node.name || node.title}</span> {hasNodeDescription && <span className="text-muted small">{node.description}</span>} </div> {/* 递归渲染子节点 */} {hasChild && childVisible && ( <ul className="list-unstyled pl-3"> {node.elements.map((childNode, idx) => ( <TreeNode key={childNode.id || idx} node={childNode} /> ))} </ul> )} </li> ); }; export default Tree;
验证要点
- 拿到接口响应后先在控制台打印,确认传入
Tree组件的是纯节点数组,结构和之前硬编码的treeData完全一致 - 确认每个节点的子节点字段名和代码中匹配(示例用的是
elements字段,如果接口用children等其他字段要同步修改) - 列表渲染记得加唯一
key,避免渲染异常
内容的提问来源于stack exchange,提问作者Valey Dauzhuk
相关产品推荐
相关产品推荐

