前言

今天来实现一个 React 的递归组件。具体的效果图如下:

图片说明

假设后端返回的数据如下:

[{        id: 1,        parent_id: 0,        name: '广东省',        children: [{                id: 2,                parent_id: 1,                name: '广州市',                children: [{                    id: 3,                    parent_id: 2,                    name: '番禺区'                }, {                    id: 4,                    parent_id: 2,                    name: '天河区'                }],            },            {                id: 7,                parent_id: 1,                name: '深圳市',                children: [{                        id: 8,                        parent_id: 7,                        name: '南山区'                    },                    {                        id: 9,                        parent_id: 7,                        name: '宝安区'                    },                    {                        id: 10,                        parent_id: 7,                        name: '龙岗区'                    },                    {                        id: 11,                        parent_id: 7,                        name: '龙华区'                    },                ],            },        ],    },    {        id: 56,        parent_id: 0,        name: '广西省',        children: [{            id: 57,            parent_id: 56,            name: '南宁市'        }, ],    },]

分析

需求

•每层元素都有一个children属性,选中这项后展示这一项所有的子菜单,首次展示或者切换时默认展示第一项子菜单•选中时高亮样式,默认为第一项高亮

实现思路

•每一层的显示逻辑应该是一样的,都是渲染这一层的列表即可•对于子菜单的渲染逻辑也类似,如果当前项有 children 属性,递归调用即可•高亮样式的逻辑:•用一个路径数组记录好当前渲染的路径,如果某一项的 id 在数组里,则高亮•切换父项时,重置数组中子项的 id ,这里可以用一个深度变量 dep 来控制

实现

渲染

•使用假数据来模拟,格式如上述•App.js中传入的dep是0,后面每渲染一层依次加1即可•每一层初始渲染时默认将第一项作为高亮节点填入路径数组中,路径数组具体如何操作在下面

//App.jsimport menuList from './mock/menu'//......this.state = {    activePath: []}//......componentDidMount() {    let { id } = this.props.list[0]    let { dep, activePath } = this.props    if (!activePath[dep]) {        this.props.changeActivePath(dep, id, this.props.list)    }}render() {    return (        

list={menuList} dep={0} //以下两个prop来控制高亮逻辑 activePath={this.state.activePath} changeActivePath={this.changeActivePath.bind(this)} /> )}

•此处为渲染一层的逻辑,拿到这一层的数据后循环渲染一下即可•标红的样式控制用上面说的路径数组来控制

//Menu.jsrender() {    let { list, dep } = this.props,        renderList = list    return (        
className="list"> {renderList.map(item => { return <li id={item.id} onClick={this.clickItem.bind(this, item.id)} className={this.props.activePath.includes(item.id) ? 'active' : ''} key={item.id}>{item.name} })} {this.renderMore(list, dep)}

)}

下面是渲染子节点的逻辑:

•先找出当前高亮的节点,要渲染它的子节点•递归调用我们的 Menu 组件即可,注意层级加1

renderMore(list, dep) {    let moreList = [], id    for (let i = 0; i < list.length; i++) {        //找出当前高亮的节点        if (list[i].id == this.props.activePath[this.props.dep]) {            moreList = list[i].children            id = list[i].id            break;        }    }    if (Array.isArray(moreList) && moreList.length > 0) {        return (            

list={moreList} dep={dep + 1} activePath={this.props.activePath} changeActivePath={this.props.changeActivePath.bind(this)} /> ) }}

切换

•切换的逻辑十分简单,将点击的id传入即可•下面具体来看路径数组的处理

clickItem(id) {    this.props.changeActivePath(this.props.dep, id, this.props.list)}

处理高亮逻辑

•如果是最后一层,则直接加入即可•如果不是,则将当前层点击的节点填入数组,重新构建下面的子节点•递归处理下子节点,默认是采用第一项作为高亮的节点

//App.jschangeActivePath(dep, id, list) {    let activePath = this.state.activePath    if (!activePath[dep] || dep == activePath.length - 1) {        //最后一个 添进去即可        activePath[dep] = id    } else {        //重新构建整个activePath数组        activePath[dep] = id        let cur = []        for (let i = 0; i < list.length; i++) {            let itemId = list[i].id            if (itemId == id) {                cur = list[i]                break            }        }        setPath(dep + 1, cur)    }    function setPath(dep, cur) {        if (cur.children) {            activePath[dep] = cur.children[0].id            setPath(dep + 1, cur.children[0])        }    }    this.setState({        activePath    })}

完整代码

App.js

import React from 'react'import Menu from './components/Menu'import menuList from './mock/menu'class App extends React.Component {    constructor(props) {        super(props)        this.state = {            activePath: []        }    }    render() {        return (            

list={menuList} dep={0} activePath={this.state.activePath} changeActivePath={this.changeActivePath.bind(this)} /> ) } changeActivePath(dep, id, list) { let activePath = this.state.activePath if (!activePath[dep] || dep == activePath.length - 1) { //最后一个 添进去即可 activePath[dep] = id } else { //重新构建整个activePath数组 activePath[dep] = id let cur = [] for (let i = 0; i < list.length; i++) { let itemId = list[i].id if (itemId == id) { cur = list[i] break } } setPath(dep + 1, cur) } function setPath(dep, cur) { if (cur.children) { activePath[dep] = cur.children[0].id setPath(dep + 1, cur.children[0]) } } this.setState({ activePath }) }}export default App

Menu.js

import React, { Component } from 'react'import '../style/Menu.less'class Menu extends Component {    constructor(props) {        super(props)    }    componentDidMount() {        let { id } = this.props.list[0]        let { dep, activePath } = this.props        if (!activePath[dep]) {            this.props.changeActivePath(dep, id, this.props.list)        }    }    renderMore(list, dep) {        let moreList = [], id        for (let i = 0; i < list.length; i++) {            if (list[i].id == this.props.activePath[this.props.dep]) {                moreList = list[i].children                id = list[i].id                break;            }        }        if (Array.isArray(moreList) && moreList.length > 0) {            return (                

list={moreList} dep={dep + 1} activePath={this.props.activePath} changeActivePath={this.props.changeActivePath.bind(this)} /> ) } } clickItem(id) { this.props.changeActivePath(this.props.dep, id, this.props.list) } render() { let { list, dep } = this.props, renderList = list return (

className="list"> {renderList.map(item => { return <li id={item.id} onClick={this.clickItem.bind(this, item.id)} className={this.props.activePath.includes(item.id) ? 'active' : ''} key={item.id}>{item.name} })} {this.renderMore(list, dep)}

) }}export default Menu

Menu.less

ul,li {    list-style: none;    margin: 0;    padding: 0;}.list {    display: flex;    li {        margin: 10px;        cursor: pointer;    }}.active {    color: red;}

react重新渲染菜单_React实现递归组件相关推荐

  1. React总结篇之六_React高阶组件

    高阶组件的概念及应用 以函数为子组件的模式 这两种方式的最终目的都是为了重用代码,只是策略不同,各有优劣,开发者可以在实际工作中决定采用哪种方式. 一.高阶组件 1. 高阶组件(Higher Orde ...

  2. vue树形权限菜单_Vue.js 递归组件实现树形菜单(实例分享)

    最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单. 项目结构: main.js 作为入口,很简单: import Vue from 'vue' Vue.config.debug = tr ...

  3. vue树形权限菜单_Vue.js 递归组件实现树形菜单

    最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单. main.js 作为入口: import Vue from 'vue' import main from './components/ ...

  4. react页面渲染之前_react 渲染顺序

    工作中要对一个表格做再次更新, 可能是渲染后更新或者部分组件渲染之后, 对页面效果做处理 之前对react的理解, 仅仅停留在render渲染. 这次好好理解了下react的生命周期 1 react组 ...

  5. react 树形菜单_react使用antd组件递归实现左侧菜单导航树

    /** 左侧导航组件*/import React, { Component }from 'react'import { Link, withRouter }from 'react-router-dom ...

  6. react 引入antd 样式_react引入antd组件

    1.antd官网: https://ant.design/docs/react/introduce-cn 2.React中使用Antd 1.安装antd npm install antd --save ...

  7. react 日期怎么格式化_react日期格式化组件

    最近在项目中经常碰到日期格式化的转化,如默认时间格式转换为"2020-02-02"或"2020-02-02"转换成"2020/02/02"等 ...

  8. React实现递归组件

    前言 今天来实现一个 React 的递归组件.具体的效果图如下: 假设后端返回的数据如下: [{id: 1,parent_id: 0,name: '广东省',children: [{id: 2,par ...

  9. mysql抽屉图标_React Native自定义组件实现抽屉菜单控件效果

    一.需求分析 原生开发中,自定义View可谓是屡见不鲜的事情,往往系统的控件总不能满足现实的需求.五花八门的产品设计需要我们做出不同的View.关于自定义View的内容网上已经有很多的博文,本篇博客要 ...

最新文章

  1. 记一次libfreenect2安装配置的经历
  2. 使用diskpart命令进行扩充硬盘空间操作说明
  3. Spring-Retry重试实现原理
  4. Python爬虫应用实战-如何对爬取的数据进行数据存储?
  5. Github标星59.7k:用动画的形式呈现解LeetCode题目的思路
  6. SQL 已死,NoSQL 才是王道?|原力计划
  7. mysql 无限上级_mysql无限上级
  8. 覆盖的面积 HDU - 1255 (扫描线, 面积交)
  9. oracle 存储过程深入学习与应用
  10. 开源3D游戏引擎Irrlicht简介
  11. 机械制图国家标准的绘图模板_JS制图:映射
  12. imac 蓝牙机械键盘_最好的蓝牙机械键盘
  13. macos复制粘贴快捷键 快速_Mac复制粘贴快捷键怎么修改 Mac复制粘贴快捷键修改步骤...
  14. 计算机看不了pdf,电脑打不开pdf格式文件怎么回事
  15. diy 文件服务器,文件服务器diy
  16. linux 应用编程(持续更新)
  17. Symbian OS s60 3rd编程中实现IMSI、IMEI查询
  18. Java常见面试题(JVM)
  19. 用JS写右下角烦人的弹窗小广告
  20. HASH 与 随机数

热门文章

  1. 【渝粤教育】电大中专电商运营实操 (25)作业 题库
  2. android 蒙层广告1,subnvue安卓机打开只显示蒙层,没有任何内容【报Bug】
  3. sqlserver select 数值精度_SQL Server读懂语句运行 (二) SET STATISTICS IO ON
  4. 简自动类型提升,精度损失类型强制转换,常用转义字符,简单帮你回顾Java基本数据类型整形浮点型字符型布尔型Boolean及其运算规则
  5. 计算机图形学结课论文,计算机图形学结课论文精要.doc
  6. java04376_Java - jdbc mybatis
  7. 鸿蒙系统6月可升级,华为鸿蒙2.0系统大规模升级从6月开始?
  8. mysql AUTO_INC 是否锁_AUTO-INC锁的一些思考
  9. 技术管理规划-从哪入手?
  10. JavaScript | JSON基本格式