React18-树形菜单-递归

文章目录

案例分析

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

从接口获取城市数据,渲染出一个树形菜单

要求:

可以展开和收起

技巧

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

通信

父子通信

展示效果

实现代码



src\views\TreeMenu\index.tsx

js 复制代码
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

js 复制代码
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

js 复制代码
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

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

相关推荐
咬人喵喵几秒前
CSS 盒子模型:万物皆是盒子
前端·css
2401_860319527 分钟前
DevUI组件库实战:从入门到企业级应用的深度探索,如何快速应用各种组件
前端·前端框架
韩曙亮40 分钟前
【Web APIs】元素滚动 scroll 系列属性 ② ( 右侧固定侧边栏 )
前端·javascript·bom·window·web apis·pageyoffset
珑墨42 分钟前
【浏览器】页面加载原理详解
前端·javascript·c++·node.js·edge浏览器
LYFlied1 小时前
在AI时代,前端开发者如何构建全栈开发视野与核心竞争力
前端·人工智能·后端·ai·全栈
用户47949283569151 小时前
我只是给Typescript提个 typo PR,为什么还要签协议?
前端·后端·开源
程序员爱钓鱼2 小时前
Next.js SSR 项目生产部署全攻略
前端·next.js·trae
程序员爱钓鱼2 小时前
使用Git 实现Hugo热更新部署方案(零停机、自动上线)
前端·next.js·trae
颜颜yan_2 小时前
DevUI + Vue 3 入门实战教程:从零构建AI对话应用
前端·vue.js·人工智能
国服第二切图仔3 小时前
DevUI Design中后台产品开源前端解决方案之Carousel 走马灯组件使用指南
前端·开源