如何将生成目录列表的JS函数转换为React JSX元素?
把标题层级数组转成React JSX元素的解决方案
嘿,我明白你不想用dangerouslySetInnerHTML的心情,毕竟这种方式既不安全也不符合React的最佳实践。咱们直接把第二个函数改成递归生成JSX的版本,完全贴合你习惯的数组映射写法~
先回顾下你的原有代码
1. 解析HTML标题为层级数组的函数
这个函数可以保留,它生成的层级数组是咱们后续处理的基础:
const str = "<h1>test-1<h1><h2>test1-1<h2><h3>test1-1-1</h3><h1>test1-2<h1><h2>test1-2-1</h2><h3>test1-2-2</h3><h1>test-2</h1><h1>test-3</h1><h1>test-4</h1> " const wrapper = document.createElement('div'); wrapper.innerHTML = str.trim(); let tree = []; let leaf = null; for (const node of wrapper.querySelectorAll("h1, h2, h3, h4, h5, h6")) { const nodeLevel = parseInt(node.tagName[1]); const newLeaf = { level: nodeLevel, text: node.textContent, children: [], parent: leaf }; while (leaf && newLeaf.level <= leaf.level) leaf = leaf.parent; if (!leaf) tree.push(newLeaf); else leaf.children.push(newLeaf); leaf = newLeaf; }
2. 原有的生成HTML列表的函数
原来的函数是生成DOM元素再转成HTML字符串,咱们要替换掉它:
const ol = document.createElement("ol"); (function makeOl(ol, leaves) { for (const leaf of leaves) { const li = document.createElement("li"); li.appendChild(new Text(leaf.text)); if (leaf.children.length > 0) { const subOl = document.createElement("ol"); makeOl(subOl, leaf.children); li.appendChild(subOl); } ol.appendChild(li); } })(ol, tree);
你之前想要避免的不安全写法:
<div dangerouslySetInnerHTML={{__html: olString}} />
而你偏好的数组映射生成JSX的方式:
const list = tree.map((headers) => <li>{headers.value}</li>) <div><ul>{list}</ul></div>
改造后的React JSX版本
咱们写一个递归的函数,直接生成React元素,完美适配你的习惯:
// 递归生成目录列表的JSX组件 const renderToc = (leaves) => { return ( <ol> {leaves.map((leaf, index) => ( <li key={index}> {leaf.text} {/* 有子节点就递归渲染子列表 */} {leaf.children.length > 0 && renderToc(leaf.children)} </li> ))} </ol> ); };
这里有几个关键点:
- 用
map遍历每个层级节点,完全符合你熟悉的写法 - 给每个
<li>加上key(React列表元素必须有唯一标识,这里用index是简单方案,如果你的标题有唯一ID之类的,用那个会更稳定) - 递归调用
renderToc处理子节点,自动生成嵌套的<ol>结构
实际用法示例
在你的React组件里直接调用这个函数就行:
function TocComponent() { // 假设你已经通过第一个函数生成了tree数组 const tree = /* 你的层级数组数据 */; return ( <div className="toc-container"> {/* 直接渲染生成的JSX */} {renderToc(tree)} </div> ); }
这样生成的都是React原生的JSX元素,完全不需要依赖dangerouslySetInnerHTML,既安全又符合React的开发模式~
内容的提问来源于stack exchange,提问作者totalnoob
相关产品推荐
相关产品推荐

