《React-classNames库》

477 阅读3分钟

需求1

原始:

我们封装了一个Icon组件,

type Props={
    name:string
}
function Icon(props:Props){
    return (
        <svg className='icon'>
            <use xlinkHref={'#'+props.name}/>
        </svg>
    )
}
export default Icon

使用:<Icon name='left'/>

添加功能

现在想点击图标,就回退到上一个页面。在使用的时候应该这样写:

 const onClickBack=()=>{
        window.history.back()
    }
<Icon name='left' onClick={onClickBack}/>

可是Icon不是一个普通的html标签,它是一个组件,如果想定义它的onClick函数,需要作为props传进去。所以现在想让Icon组件接受一个onClick的外部属性。

考虑到onClick就是标签自带的属性,我们可以在Icon组件里使用&继承语法,让这个组件接受的props直接继承SVG标签自带的所有属性。

type Props={
    name:string
}& React.SVGAttributes<SVGElement>          // 1. 继承
function Icon(props:Props){
    const {name,children,...rest}=props     // 2. 从props里取出
    return (
        <svg className='icon' {...rest}>    // 3. 把props传进来的属性复制到svg上
            <use xlinkHref={'#'+props.name}/>
        </svg>
    )
}
export default Icon

在Icon组件里这样做了以后,使用它的时候,就可以传任意的svg标签支持的属性写法,Icon组件都能通过props接收到,并且用在svg标签上。

className的覆盖

可是用rest有一个问题,如果我在外边给Icon传了一个className,但是svg标签本来就自带了一个icon的className,那么rest里的className就会覆盖svg本来写死的className。<Icon name='left' onClick={onClickBack} className='xxx'/>比如这样,svg的className就变成了xxx。

1. 把className排除

//Icon.tsx
const {name,children,className,...rest}=props

从props里拿出各种属性时,把className单拿出来,这样rest里就没有className了,就不会覆盖原来的值了。即使使用Icon时传了className,由于rest里没有className,也不会生效。

但是,我外边还是想传className,让里边的svg拥有两个className,以空格连接的那种。

2. 插值

type Props={
    name:string
}& React.SVGAttributes<SVGElement>
function Icon(props:Props){
    const {name,children,className,...rest}=props
    return (
        <svg className={`icon ${className} `} {...rest}>
            <use xlinkHref={'#'+props.name}/>
        </svg>
    )
}
export default Icon

直接自定义,className是一个icon字符串 空格 再加上className变量的值

<Icon name='left' onClick={onClickBack} className='xxx'/>

从页面可以观察到svg的className是icon xxx 成功了。

又一个bug:如果我没传className,那svg的实际className就变成了:

icon undefined 因为没给传。

怎么办呢?判断一下className有没有:

<svg className={`icon ${className? className:''} `} {...rest}>

问题:这也太麻烦了

3. classnames 库

安装:

yarn add classnames             源代码库
yarn add --dev @types/classnames   ts支持

安装之后发现两个版本号不一致,一个是2.2.6 一个是2.2.10,保险起见,最好改成一样的。去package.json里修改,把版本前边的^删掉,把2.2.10改成2.2.6 ,然后运行yarn install

使用:

import classnames from 'classnames';

type Props={
    name:string
}& React.SVGAttributes<SVGElement>
function Icon(props:Props){
    const {name,children,className,...rest}=props
    return (
        <svg className={classnames('icon' , className)} {...rest}>
            <use xlinkHref={'#'+props.name}/>
        </svg>
    )
}
export default Icon

首先明确,我们需要一个icon字符串形式的类,和一个className变量形式的类。使用classnames 它可以同时支持不同的形式,会自动在他们中间加上空格。

如果传了className属性,svg的类就是icon xxx ,如果没传,也不会出现undefined,而是只有一个icon

import cs from 'classnames';

可以使用cs 简化代码

2.

怎么看页面是否刷新?

  1. 看开发者工具的Network,如果刷新了,会有新的网络请求。
  2. 在入口文件index.tsx里打log,如果刷新了,就会有log