React18-树形菜单-递归

案例分析

https://github.com/dL-hx/manager-fe/commit/85faf3b1ae9a925513583feb02b9a1c87fb462f7

从接口获取城市数据,渲染出一个树形菜单
要求:
可以展开和收起

技巧

  1. 学会递归渲染出一个树形菜单, 并点击后可以展开和收起
  2. 通过对数据上添加控制字段(show)避免在子组件添加各种同步的useState,等字段.保证单向数据流向, 来简化问题.

通信

父子通信

展示效果

在这里插入图片描述

实现代码

在这里插入图片描述
在这里插入图片描述
src\views\TreeMenu\index.tsx

import TreeMenu from "./TreeMenu";
export default TreeMenu

技巧点

  1. 调用pulic/citydata.json数据
  2. 通过原数据添加show字段,递归处理.为每一个item添加show字段
  3. 当再次点击item选项时候,根据原数据上的show字段进行控制, 递归修改源数据对应的id(判断条件找到对应item, if (item.value === id) ).
    将其取反后setData
    对于接口源数据没有返回的ui控制字段,前端需要自己添加

因为是react, 对引用数据修改要注意需要生成新数据后在setData
src\views\TreeMenu\TreeMenu.tsx

import {
    Button } from "antd";
import {
    useState, useEffect } from "react";
import Tree from "./Tree";
export default function TreeMenu() {
   
    const [data, setData] = useState([])

    useEffect(() => {
   
        fetch('/citydata.json').then(res => res.json()).then(res => {
   
            setData(recursionAddProps(res))
        })
    }, [])

    // 当数据请求过来后,我们要递归式给每一个数据加一个属性,用来控制是否展开功能
    const recursionAddProps = (data) => {
   
        // 在item 上加一个属性
        // show: true 则展开,  false 折叠
        return data.map((item) => {
   
            item.show = false
            // 递归式自己调用自己
            if (item.children) {
   
                recursionAddProps(item.children)
            }
            return item
        })
    }

    /**
     * 
     * @param data 源数据
     * @param id 点击的id选项
     * @returns 
     */
    const recursionEditProps = (data, id) => {
   
        // 在item 上加一个属性
        // show: true 则展开,  false 折叠
        return data.map((item) => {
   
            if (item.value === id) {
    // 判断条件: 找到对应id在取反
                item.show = !item.show // 取反
            } 
            // 递归式自己调用自己
            if (item.children) {
   
                recursionEditProps(item.children, id)
            }
            return item
        })
    }

    // 获取到子组件传递过来的数据
    const setShow = (child) => {
   
        // item.show = true
        // const newData = data.map((item) => {
   
        //     if (item['value'] === child.value) {
   
        //         // @ts-ignore
        //         item['show'] = !child.show
        //     }
        //     return item
        // })
        // setData(newData)

        // 需要递归处理这个show展开 
        // console.log(recursionEditProps(data, child.value));
        // 通过item-id,把数据中的show 改为true
        setData(recursionEditProps(data, child.value))
    }
    return <div>
        <Tree data={
   data} setShow={
   setShow}></Tree>
    </div>
}

技巧点

  1. 递归调用自身
  2. 通过为父组件原数据添加show字段, 子组件修改父组件数据结构的show字段来控制,子组件是否展示
    因为是单向数据流, 数据控制视图, 通过递归完成

src\views\TreeMenu\Tree.tsx

import Tree from './Tree'

export default ({
     data, show=true, setShow }: any) => {
   
    console.log(data);

    return <ul style={
   {
   display: show? 'block': 'none'}}>
        {
   
            Array.isArray(data) && data.length && data.map(item => {
   
                return <li key={
   item.value} onClick={
   (event)=>{
   
                    // 清除冒泡
                    event.stopPropagation()
                    setShow(item)
                }}>
                    {
   item.value}
                    {
   /* 递归组件 */}
                    {
   
                        item.children && item.children.length && <Tree data={
   item.children} show={
   item.show} setShow={
   setShow}/>
                    }
                </li>
            })
        }
    </ul>
}

/public/citydata.json

[
    {
   
        "lable": "北京市",
        "value": "北京市",
        "children": [
            {
   
                "lable": "东城区",
                "value": "东城区",
                "children": [
                    {
   
                        "lable": "东城区-0",
                        "value": "东城区-0"
                    },
                    {
   
                        "lable": "东城区-1",
                        "value": "东城区-1"
                    },
                    {
   
                        "lable": "东城区-2",
                        "value": "东城区-2"
                    }
                ]
            },
            {
   
                "lable": "西城区-1",
                "value": "西城区",
                "children": [
                    {
   
                        "lable": "西城区-0",
                        "value": "西城区-0"
                    },
                    {
   
                        "lable": "西城区-1",
                        "value": "西城区-1"
                    },
                    {
   
                        "lable": "西城区-2",
                        "value": "西城区-2"
                    }
                ]
            },
            {
   
                "lable": "海淀区-2",
                "value": "海淀区-2"
            }
        ]
    },
    {
   
        "lable": "西安市",
        "value": "西安市",
        "children": [
            {
   
                "lable": "长安区-0",
                "value": "长安区-0"
            },
            {
   
                "lable": "桥西区-1",
                "value": "桥西区-1"
            },
            {
   
                "lable": "新华区-2",
                "value": "新华区-2"
            }
        ]
    },
    {
   
        "lable": "天津市",
        "value": "天津市"
    }
]

Refer to

https://www.bilibili.com/video/BV19W4y1p7eu

相关推荐

  1. 树形模板

    2024-01-17 01:04:02       55 阅读
  2. C# 获取树形结构数据类型

    2024-01-17 01:04:02       97 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-17 01:04:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-17 01:04:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-17 01:04:02       82 阅读
  4. Python语言-面向对象

    2024-01-17 01:04:02       91 阅读

热门阅读

  1. C++进阶--unordered_set、unordered_map的介绍和使用

    2024-01-17 01:04:02       47 阅读
  2. 删除排序链表中的重复元素

    2024-01-17 01:04:02       56 阅读
  3. 【leetcode100-035】【链表/哈希链表】LRU缓存

    2024-01-17 01:04:02       60 阅读
  4. 链路聚合【华为与新华三的区别】

    2024-01-17 01:04:02       45 阅读
  5. Linux 使用小记

    2024-01-17 01:04:02       65 阅读