前言

今天来实现一个 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.js
import menuList from './mock/menu'
//......
this.state = {activePath: []
}
//......
componentDidMount() {let { id } = this.props.list[0]let { dep, activePath } = this.propsif (!activePath[dep]) {this.props.changeActivePath(dep, id, this.props.list)}
}
render() {return (<Menulist={menuList}dep={0}//以下两个prop来控制高亮逻辑activePath={this.state.activePath}changeActivePath={this.changeActivePath.bind(this)}/>)
}
  • 此处为渲染一层的逻辑,拿到这一层的数据后循环渲染一下即可
  • 标红的样式控制用上面说的路径数组来控制
//Menu.js
render() {let { list, dep } = this.props,renderList = listreturn (<div><ul 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}</li>})}</ul>{this.renderMore(list, dep)}</div>)
}

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

  • 先找出当前高亮的节点,要渲染它的子节点
  • 递归调用我们的 Menu 组件即可,注意层级加1
renderMore(list, dep) {let moreList = [], idfor (let i = 0; i < list.length; i++) {//找出当前高亮的节点if (list[i].id == this.props.activePath[this.props.dep]) {moreList = list[i].childrenid = list[i].idbreak;}}if (Array.isArray(moreList) && moreList.length > 0) {return (<Menulist={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.js
changeActivePath(dep, id, list) {let activePath = this.state.activePathif (!activePath[dep] || dep == activePath.length - 1) {//最后一个 添进去即可activePath[dep] = id} else {//重新构建整个activePath数组activePath[dep] = idlet cur = []for (let i = 0; i < list.length; i++) {let itemId = list[i].idif (itemId == id) {cur = list[i]break}}setPath(dep + 1, cur)}function setPath(dep, cur) {if (cur.children) {activePath[dep] = cur.children[0].idsetPath(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 (<Menulist={menuList}dep={0}activePath={this.state.activePath}changeActivePath={this.changeActivePath.bind(this)}/>)}changeActivePath(dep, id, list) {let activePath = this.state.activePathif (!activePath[dep] || dep == activePath.length - 1) {//最后一个 添进去即可activePath[dep] = id} else {//重新构建整个activePath数组activePath[dep] = idlet cur = []for (let i = 0; i < list.length; i++) {let itemId = list[i].idif (itemId == id) {cur = list[i]break}}setPath(dep + 1, cur)}function setPath(dep, cur) {if (cur.children) {activePath[dep] = cur.children[0].idsetPath(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.propsif (!activePath[dep]) {this.props.changeActivePath(dep, id, this.props.list)}}renderMore(list, dep) {let moreList = [], idfor (let i = 0; i < list.length; i++) {if (list[i].id == this.props.activePath[this.props.dep]) {moreList = list[i].childrenid = list[i].idbreak;}}if (Array.isArray(moreList) && moreList.length > 0) {return (<Menulist={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 = listreturn (<div><ul 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}</li>})}</ul>{this.renderMore(list, dep)}</div>)}
}
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实现递归组件相关推荐

  1. react重新渲染菜单_React实现递归组件

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

  2. react 动态添加组件属性_这么高质量React面试题(含答案),看到就是赚到了!...

    前言 本文篇幅较长,全是干货,建议亲们可以先收藏慢慢看哦 写文不易,欢迎大家一起交流,喜欢文章记得关注我点个赞哟,感谢支持! Q1 :什么是虚拟DOM? 难度::star: 虚拟DOM(VDOM)它是 ...

  3. react实现汉堡_利用 React 高阶组件实现一个面包屑导航

    什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁, ...

  4. 这就是为什么我们需要在React的类组件中绑定事件处理程序

    by Saurabh Misra 索拉·米斯拉(Saurabh Misra) 这就是为什么我们需要在React的类组件中绑定事件处理程序 (This is why we need to bind ev ...

  5. 从0到1,一步步开发React的loading组件,并发布到npm上

    没有发布过npm包的同学,可能会对NPM对开发有一种蜜汁敬畏,觉得这是一个很高大上的东西.甚至有次面试,面试官问我有没有发过npm包,当时只用过还没写过,我想应该挺难的,就小声说了没有,然后就让我回去 ...

  6. Java 接受reactjs数据_[Java教程]react.js 父子组件数据绑定实时通讯

    [Java教程]react.js 父子组件数据绑定实时通讯 0 2017-09-23 17:00:14 import React,{Component} from 'react'import Reac ...

  7. react中高阶组件

    react高阶组件深入理解.作用以及应用 本文主要以通俗易懂的语言表达自己对高阶组件的一些见解,希望大家多多提问 高阶组件深入理解 高阶组件就是一个函数,传给它一个组件,它返回一个新的组件.新的组件使 ...

  8. vue树形结构html,怎么在vue中利用递归组件实现一个树形控件

    怎么在vue中利用递归组件实现一个树形控件 发布时间:2021-06-11 17:26:48 来源:亿速云 阅读:81 作者:Leah 本篇文章为大家展示了怎么在vue中利用递归组件实现一个树形控件, ...

  9. React Native 下载组件以及npm常用命令

    一.React Native 下载组件: RN的组件都是需要从网上下载的.正常来说,我们通过npm start打开服务器之后,直接用npm下载即可.常用的组件,例如按钮,滚动等,都是可以直接下载的.下 ...

最新文章

  1. linux下epoll如何实现高效处理
  2. Activiti多人会签例子
  3. springboot配置跨mapper.xml的全局变量
  4. eclipse:项目启动MySQL报错:The last packet successfully received from the server was x milliseconds ago
  5. OpenMV(五)--STM32实现人脸识别
  6. ml302硬件手册_Cat.1模组ML302使用MQTT协议接入OneNet平台
  7. 深度学习论文和开源代码
  8. 20155213 实验三《敏捷开发与XP实践》实验报告
  9. 基于PHP+MySQL图书管理系统的设计与实现
  10. Linux忘记root密码重置密码方法
  11. 阿里矢量图标(字体图标)
  12. 组合数学在软件领域的运用
  13. python编写udp端口扫描工具全
  14. H5页面唤起指定app或跳转到应用市场
  15. 互联网公司发送短信为什么通过第三方短信平台,而不是通过运营商。
  16. VPP线程之间报文调度
  17. 超融合详细对比:市面各主流超融合产品及厂商优劣势解密
  18. linux下dbus的理解学习
  19. 指向类成员函数的指针(学自王桂林)
  20. three.js入门——画一个3D正方体

热门文章

  1. 0528班宋ww:回顾刚来的那一天还历历在目,不禁感概一番
  2. 2021高考体检成绩查询,2021年重庆高考体检报告结果查询时间及查询网址入口
  3. Java+MySql存储表情符
  4. ZooZ推出应用内移动支付SDK
  5. 全景制作教程:如何利用Pano2VR进行补天补地?
  6. android手机连接esp32视频
  7. ACM纪念日 C语言
  8. 类别名称转换为一一对应的数字标签
  9. e语言做爱奇艺视频采集_现有的15种奇异(和疯狂)编程语言
  10. php 实现 pacs 系统,知名PACS系统VC源码