跳至主要內容

容器内选择单一列表并进行高亮切换的实践

Yihui大约 2 分钟

容器内选择单一列表并进行高亮切换的实践

基于React和antd技术栈下,对有限容器内列表选择的行进行高亮提示,并且不影响容器内部进行的滚动查看引发的思考,涉及的内容包括overflow熟悉,CSS 类名替换而不是改写style、React Element的复用和性能优化。

前置知识

overflow

overflow 是overflow-x 和overflow-y的简写,用来设定当一块级元素(通常设置了height/max-height)的内容太大而超出范围的时候,元素内容如何加载。

overflow:属性值;
overflow: [overflow-x] [overflow-y];

只设定一个值的话,则同时对x和y轴生效,当其中一方被设置了auto的话,visible的表现也会是auto

其他用途

  • text-overflow 与 overflow 配合用于文字省略

    超出文字隐藏:overflow:hidden; 文字不换行:text-wrap:nowrap;

  • overflow: hidden 清除浮动

  • overflow: hidden 解决外边距折叠问题(触发BFC)

分析实践

CSS 类名替换而不是改写style

并不适用于虚拟列表

demo 演示

设置容器、列表样式以及选中行的样式:

.main {
    background: grey;
    margin: auto;
    width: 50vw;
    height: 400px;
    overflow: auto;
}

.list {
    width: 100vw;
    height: 100vw;
}

.active {
    background: aqua;
}
// App.jsx
import React, {useState} from 'react';
import './App.css';
import {List} from "antd";

const data = [
    'Racing car sprays burning fuel into crowd.',
    'Japanese princess to wed commoner.',
    'Australian walks 100km after outback crash.',
    'Man charged over missing wedding girl.',
    'Los Angeles battles huge wildfires.',
];

function App() {
    const [activeIndex, setActiveIndex] = useState(-1)
    return (
        <div className={"main"}>
            <div className={"list"}>
                <List
                    size="small"
                    bordered
                    dataSource={data}
                    renderItem={(item, index) => (
                        <List.Item
                            className={index === activeIndex ? "active" : ""}
                            onClick={() => {
                                setActiveIndex(index)
                            }}>{item}</List.Item>
                    )}
                />
            </div>
        </div>
    );
}

export default App;

显示效果

通过设置外部容器固定宽高,overflow: auto已经生效,生成滚动条

image-20220818094857915

外部内容被隐藏遮住

image-20220818100627657

当点击的时候,改变的是仅受影响的两行的类名

image-20220818095042385

image-20220818095006325

此修改方法通过只修改类名而不是style属性,更有利于后期维护和修改选中的样式,我们只需对.active进行修改即可。如果是写成style形式,就很冗余了,也不好扩展。

 style={index === activeIndex ? {backgroundColor: '#f5f5f5'} : {}}

思考问题

数据量大了怎么办?目前修改相当于整个App都被重新执行了渲染,行数多了有明显卡顿,甚至没有反应,而且生成的时候这行相当于进行多了次比较:

className={activeIndex === index ? "active" : ""}

Mark一下,待思考解决办法:

只对选中行进行重绘

通过标记之前选中的节点,在下次选中的时候进行重写className

    let preClickElement = null
    const handleClick = useCallback((e) => {
        const target = e.currentTarget
        target.className = 'ant-list-item active';
        preClickElement && (preClickElement.className = 'ant-list-item')
        preClickElement = target;
    }, [])

demo

{
    data.map((item, index) => {
        return <List.Item
            key={index}
            onClick={handleClick}>{item}</List.Item>
    })
}

如果用虚拟列表的话,如何实现这个效果?

Mark,重写一个虚拟列表demo后尝试