能否使用Normalizr构建n深度树形结构并实现双向转换?
使用Normalizr实现扁平数据与嵌套树形结构的双向转换
嘿,我完全懂你纠结Normalizr用法的心情——很多人刚接触它时,都会混淆它的适用场景,尤其是想用它做嵌套树形转换的时候。其实Normalizr不仅能把嵌套数据扁平化(这是它最常见的用法,比如给Redux存数据),反过来用它的Schema和实体系统,也能轻松实现扁平数据到嵌套结构的转换,还能把树形结构再转回去。咱们一步步来实现你要的功能:
第一步:定义Normalizr Schema
首先得为你的节点数据定义Schema,因为你的数据是层级嵌套的,每个节点的children是同类型的节点数组,所以我们需要递归引用Schema:
import { schema, normalize, denormalize } from 'normalizr'; // 定义单个节点的Schema const nodeSchema = new schema.Entity('nodes', { // 递归引用自身,说明children是节点数组 children: [nodeSchema] }); // 顶层是节点数组,所以定义一个数组Schema const nodeListSchema = [nodeSchema];
第二步:扁平数据转嵌套树形结构
你的原始扁平数据里,children是包含id的对象数组(比如[{id: 3076}]),而Normalizr更习惯直接处理id数组,所以先做个简单预处理,把children转成纯id数组:
const rawData = [ { "id": 1077, "title": "Hello world", "children": [{ "id": 3076 }] }, { "id": 3076, "title": "Hello foo", "children": [{ "id": 3077 }] }, { "id": 3077, "title": "Hello bar", "children": [] }, { "id": 3078, "title": "Goodbye world", "children": [] } ]; // 预处理:将children从对象数组转为id数组 const flattenedData = rawData.map(item => ({ ...item, children: item.children.map(child => child.id) }));
接下来,我们先把预处理后的扁平数据标准化(这一步会生成Normalizr需要的entities实体库和result顶层id列表),再用denormalize方法还原成嵌套结构:
// 标准化扁平数据,得到实体库和顶层id列表 const normalized = normalize(flattenedData, nodeListSchema); // 从实体库还原出嵌套树形结构 const nestedTree = denormalize(normalized.result, nodeListSchema, normalized.entities); console.log(nestedTree); // 输出就是你想要的嵌套树形结构: // [ // { "id": 1077, "title": "Hello world", "children": [{ "id": 3076, "title": "Hello foo", "children": [{ "id": 3077, "title": "Hello bar", "children": [] }] }] }, // { "id": 3078, "title": "Goodbye world", "children": [] } // ]
第三步:嵌套树形结构转扁平数据(用于发送给服务器)
这一步就简单了,直接用normalize方法处理嵌套树形结构,得到标准化后的实体库,再把实体库转成你需要的扁平格式(如果需要把id数组转回对象数组的话):
// 假设nestedTree是你的嵌套树形结构 const normalizedBack = normalize(nestedTree, nodeListSchema); // 提取实体库中的所有节点,再把children转回对象数组(匹配服务器需要的格式) const flattenedForServer = Object.values(normalizedBack.entities.nodes).map(node => ({ ...node, children: node.children.map(id => ({ id })) })); console.log(flattenedForServer); // 输出就是和你原始输入一致的扁平数据
关键说明
- Normalizr的核心是管理关联数据的引用,它帮你省去了自己写递归遍历、匹配id的麻烦
- 递归Schema的定义是实现层级转换的关键,一定要确保
children字段引用的是自身的数组Schema - 如果你的服务器返回/接收的直接是
id数组(而非{id: xxx}对象数组),那预处理和后处理的步骤可以省略
这样用下来,你应该能清晰感受到Normalizr在处理这类关联数据时的便捷性了~
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

