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

前言

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

ceb91f38d847d6cb1d6e3a27b7e452aa.gif
图片说明

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

[{        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;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript测验——给代码添加注释---第一关

校验规则 创建一个//样式的注释, 被注释的文本至少要包含 5 个字符。 创建一个/* */样式的注释, 被注释的文本至少要包含 5 个字符。

举重设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#xff01; 目录 …

JavaScript测验——声明变量---第2关

校验规则 注意: 变量名称可以由数字、字母、美元符号$ 或者 下划线_组成&#xff0c;但是不能包含空格或者以数字为开头。 闯关: 使用var 关键字来创建一个名为salePrice的变量。

palapaweb怎样开启服务_为什么说微服务,要从前后端分离开始?一文带你揭秘深入微服务...

前言既要低头赶路&#xff0c;又要抬头望天&#xff0c;科技是为人服务的&#xff0c;任何技术背后都有更深层次的考量。之前的文章中咱们聊了很多微服务的相关内容&#xff0c;简而言之&#xff0c;微服务的本质&#xff0c;就是一种可以加速分工、促进合作的新协作机制。知其…

list集合

List集合 List集合的概述 有序集合&#xff08;也称之为序列&#xff09;&#xff0c;用户可以精确的控制列表中的每个元素的插入位置。用户可以通过整数索引访问元素&#xff0c;并搜索列表中的元素 与 Set 集合不同&#xff0c;列表通常允许重复的元素 List 集合的特点 有…

JavaScript测验——使用赋值运算符---第3关

校验规则 以上代码数值8被赋给变量myVar中&#xff0c;然后再次将变量myVar解析为8并将其赋给myNum变量。 闯关&#xff1a; 把数值6赋给变量 x。 然后把变量x中的内容赋给变量y。

antd table排序 vue_商品品牌业务之Vue编写前端页面

今天是刘小爱自学Java的第145天。感谢你的观看&#xff0c;谢谢你。学习计划安排如下&#xff1a;打算从前端页面到后台服务器代码完整地写一遍&#xff0c;但显然我高估了自己的实力&#xff0c;几个小时的时间根本不够用。并且因为教程和vue现在最新的组件用法不一样&#xf…

JavaScript测验——使用赋值运算符初始化变量---第4关

通常在声明变量的时候会给变量初始化一个初始值。 例如: var myVar 0; 以上代码创建一个名为myVar的变量并指定一个初始值0。

Zabbix-3.0.0 安装Graphtree

导读Zabbix中&#xff0c;想要集中展示图像&#xff0c;唯一的选择是screen&#xff0c;后来zatree解决了screen的问题&#xff0c;但性能不够好。Graphtree 由OneOaaS开发并开源出来&#xff0c;用来解决Zabbix的图形展示问题&#xff0c;性能较好。一、Graphtree功能概述集中…

装饰器设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#xff01; 目录 …

JavaScript测验——未初始化的变量---第5关

校验规则 当你用一个值是undefined的变量来做字符串拼接操作的时候&#xff0c;它会输出字符串"undefined"。 闯关: 定义 3 个变量a、b、c&#xff0c;并且分别给他们赋值&#xff1a;3、9、"I am pretty"&#xff0c;这样它们值就不会是undefined了。

修改telnet提示并非_俊翔:修改ECU数据解除奔驰GL350尿素限制

为达到排放要求&#xff0c;很多乘用柴油车都要加尿素(Adblue)&#xff0c;以降低废气排放。在轿车维修当中&#xff0c;最常见的有奔驰GL350、路虎揽胜出现尿素锁定。仪表上提示加尿素&#xff0c;并且限制启动次数。每当把发动机熄火再启动一次&#xff0c;次数就会减少&…

JavaScript测验——变量名大小写---第6关

校验规则 例如&#xff1a; var userName; var userLoginFlag;闯关: 修改已声明的变量&#xff0c;让它们的命名符合驼峰命名法的规范。 这个需要注意的是在声明和赋值时都应该使用驼峰命名法。

redux异步action_React躬行记(12)——Redux中间件

Redux的中间件&#xff08;Middleware&#xff09;遵循了即插即用的设计思想&#xff0c;出现在Action到达Reducer之前&#xff08;如图10所示&#xff09;的位置。中间件是一个固定模式的独立函数&#xff0c;当把多个中间件像管道那样串联在一起时&#xff0c;前一个中间件不…

监控与管理

本文是我们名为“ Spring Integration for EAI ”的学院课程的一部分。 在本课程中&#xff0c;向您介绍了企业应用程序集成模式以及Spring Integration如何解决它们。 接下来&#xff0c;您将深入研究Spring Integration的基础知识&#xff0c;例如通道&#xff0c;转换器和适…

郑州智慧岛大数据管委会_数据科学融通应用数学 ‖ 智慧岛大讲堂

8月6日上午&#xff0c;郑东新区智慧岛大数据实验区管委会&#xff0c;举办了“数据科学融通应用数学”专题智慧岛大讲堂讲座&#xff0c;邀请到北京大学数学科学学院副教授、大数据分析与应用技术国家工程实验室郑州数字创新中心主任卢朓为本次大讲堂活动做主题分享&#xff0…

db2 获取返回的游标_MySQL ------ 存储过程与游标简单使用

存储过程小例子如完成以下事情&#xff0c;获得与之前一样的订单合计&#xff0c;但需要对合计增加营业税&#xff0c;不过只针对某些顾客主要就是&#xff1a;1、获得合计2、把营业税有条件的添加到合计 3、返回合计&#xff08;带或不带税&#xff09;delimiter $$-- 存储过程…