青训营大项目 | 组件库增加size属性

84 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天

实现

import * as React from 'react';

export type SizeType = 'small' | 'middle' | 'large' | undefined;

const SizeContext = React.createContext<SizeType>(undefined);

export interface SizeContextProps {
    size?: SizeType;
    children?: React.ReactNode;
}

export const SizeContextProvider: React.FC<SizeContextProps> = ({ children, size }) => (
    <SizeContext.Consumer>
        {originSize => (
            <SizeContext.Provider value={size || originSize}>{children}</SizeContext.Provider>
        )}
    </SizeContext.Consumer>
);

export default SizeContext;

创建一个 Context 对象,当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

Context.Consumer

    <SizeContext.Consumer>
        {originSize => (
               // ...
        )}
    </SizeContext.Consumer>

这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue

使用

确定props类型

export interface TabsProps {
    className?: string,
    style?: CSSProperties,
    size: SizeType // 'small' | 'middle' | 'large' | undefined;
    //....
}

编写内容

const Tabs: React.FC<TabsProps> = (props) => {
    const {
        size:propSize
    } = props
    return (
        <SizeContext.Consumer>
            {
                contextSize => {
                    const size = propSize !== undefined ? propSize : contextSize;
                    return(
                        <div className={`${prefixCls}-${size}`}></div>
                    )
                }
            }
        </SizeContext.Consumer>
    )
}

组件具有多种状态,${prefixCls}-${size}这种写法对现在的情景是没有问题的,但是出现隐藏和显示两种状态的是否,我们可能会使用的到三元表达式等,这样让className变得难以阅读

所以,我们选择安装依赖classnames来处理该问题:

let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true }); // btn-primary
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

classnames 文档

编写样式

@tab-prefix-cls: ~'@{romantic-prefix}-tabs';

.@{tab-prefix-cls} {
  &-small {
    > .@{tab-prefix-cls}-nav {
      .@{tab-prefix-cls}-tab {
        padding: @tabs-horizontal-padding-sm;
        font-size: @tabs-title-font-size-sm;
      }
    }
  }

  &-large {
    > .@{tab-prefix-cls}-nav {
      .@{tab-prefix-cls}-tab {
        padding: @tabs-horizontal-padding-lg;
        font-size: @tabs-title-font-size-lg;
      }
    }
  }
}