React实现递归组件
前言
今天来实现一个 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实现递归组件相关推荐
- react重新渲染菜单_React实现递归组件
前言 今天来实现一个 React 的递归组件.具体的效果图如下: 图片说明 假设后端返回的数据如下: [{ id: 1, parent_id: 0, name: '广东省', children: [{ ...
- react 动态添加组件属性_这么高质量React面试题(含答案),看到就是赚到了!...
前言 本文篇幅较长,全是干货,建议亲们可以先收藏慢慢看哦 写文不易,欢迎大家一起交流,喜欢文章记得关注我点个赞哟,感谢支持! Q1 :什么是虚拟DOM? 难度::star: 虚拟DOM(VDOM)它是 ...
- react实现汉堡_利用 React 高阶组件实现一个面包屑导航
什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁, ...
- 这就是为什么我们需要在React的类组件中绑定事件处理程序
by Saurabh Misra 索拉·米斯拉(Saurabh Misra) 这就是为什么我们需要在React的类组件中绑定事件处理程序 (This is why we need to bind ev ...
- 从0到1,一步步开发React的loading组件,并发布到npm上
没有发布过npm包的同学,可能会对NPM对开发有一种蜜汁敬畏,觉得这是一个很高大上的东西.甚至有次面试,面试官问我有没有发过npm包,当时只用过还没写过,我想应该挺难的,就小声说了没有,然后就让我回去 ...
- Java 接受reactjs数据_[Java教程]react.js 父子组件数据绑定实时通讯
[Java教程]react.js 父子组件数据绑定实时通讯 0 2017-09-23 17:00:14 import React,{Component} from 'react'import Reac ...
- react中高阶组件
react高阶组件深入理解.作用以及应用 本文主要以通俗易懂的语言表达自己对高阶组件的一些见解,希望大家多多提问 高阶组件深入理解 高阶组件就是一个函数,传给它一个组件,它返回一个新的组件.新的组件使 ...
- vue树形结构html,怎么在vue中利用递归组件实现一个树形控件
怎么在vue中利用递归组件实现一个树形控件 发布时间:2021-06-11 17:26:48 来源:亿速云 阅读:81 作者:Leah 本篇文章为大家展示了怎么在vue中利用递归组件实现一个树形控件, ...
- React Native 下载组件以及npm常用命令
一.React Native 下载组件: RN的组件都是需要从网上下载的.正常来说,我们通过npm start打开服务器之后,直接用npm下载即可.常用的组件,例如按钮,滚动等,都是可以直接下载的.下 ...
最新文章
- linux下epoll如何实现高效处理
- Activiti多人会签例子
- springboot配置跨mapper.xml的全局变量
- eclipse:项目启动MySQL报错:The last packet successfully received from the server was x milliseconds ago
- OpenMV(五)--STM32实现人脸识别
- ml302硬件手册_Cat.1模组ML302使用MQTT协议接入OneNet平台
- 深度学习论文和开源代码
- 20155213 实验三《敏捷开发与XP实践》实验报告
- 基于PHP+MySQL图书管理系统的设计与实现
- Linux忘记root密码重置密码方法
- 阿里矢量图标(字体图标)
- 组合数学在软件领域的运用
- python编写udp端口扫描工具全
- H5页面唤起指定app或跳转到应用市场
- 互联网公司发送短信为什么通过第三方短信平台,而不是通过运营商。
- VPP线程之间报文调度
- 超融合详细对比:市面各主流超融合产品及厂商优劣势解密
- linux下dbus的理解学习
- 指向类成员函数的指针(学自王桂林)
- three.js入门——画一个3D正方体