In the previous work experience, we have always used Antd as the basic component for development. Now we have to implement a UI component library for managing the background. When writing the Tabs component, we borrowed the ideas of Antd, mainly using React Context characteristics, documented here

It mainly shows the core idea (only the controlled components are implemented), and the style details are skipped. The renderings are as followsimage.png

  • How to use

     const [value, setValue] = useState('1')
    
    <Tabs
      activeKey={value}
      isCache={true}
      onChange={value => setValue(value)}
    >
      <Tabs.Panel tabKey='1' tab='Tab Item'><div>11111</div></Tabs.Panel>
      <Tabs.Panel tabKey='1' tab='Tab Item'><div>11111</div></Tabs.Panel>
    </Tabs>
  • Component export module, implementation <Tabs.Panel></Tabs.Panel> use this form

     // 模仿 antd 实现 https://github.com/ant-design/ant-design/blob/master/components/radio/index.tsx
    import TabsComps, { TTabsProp } from './Tabs'
    import Panel from './Panel'
    
    interface CompoundedComponent
      extends React.ForwardRefExoticComponent<
        TTabsProp & React.RefAttributes<HTMLElement>
      > {
      Panel: typeof Panel
    }
    
    const Tabs = TabsComps as CompoundedComponent
    Tabs.Panel = Panel
    export default Tabs
  • context implementation

     import React from 'react'
    
    const TabsContext = React.createContext<any>(null)
    
    export default TabsContext
  • Panel component implementation

     import { ReactElement, useContext } from 'react'
    import cn from 'classnames'
    import TabsContext from './context'
    
    export type TPanelProp = {
      tab: string | ReactElement
      tabKey: string | number
    }
    // 此处需要注意的是 Tab.Panel 的 children 属性交由 Tabs 组件处理
    export default function Panel({ tab, tabKey }: TPanelProp): JSX.Element {
      const tabCtx = useContext(TabsContext)
      return (
        <div
          className={cn(tabCtx.activeKey === tabKey ? 'active' : '')}
          onClick={() => tabCtx.onChange(tabKey)}
        >
          {tab}
        </div>
      )
    }
  • Tabs implementation

     import { ReactElement } from 'react'
    import cn from 'classnames'
    import TabsContext from './context'
    
    export type TTabsProp = {
      isCache?: boolean // 是否用 display 隐藏元素
      children: ReactElement[]
      activeKey: string | number
      onChange?: (value: any) => void
    }
    
    export default function Tabs({
      isCache = true,
      children,
      activeKey,
      onChange,
    }: TTabsProp): JSX.Element {
      let panel = null
      if (isCache) {
        panel = children?.map(v => {
          const tabKey = v.props.tabKey
          return (
            <div key={tabKey} className={tabKey === activeKey ? '' : 'hidden'}>
              {v.props.children}
            </div>
          )
        })
      } else {
        panel = children.find(v => v.props?.tabKey === activeKey)?.props?.children
      }
    
      return (
        <>
          <div>
            <TabsContext.Provider value={{ activeKey, onChange }}>
              {children}
            </TabsContext.Provider>
          </div>
          <div>{panel}</div>
        </>
      )
    }

大桔子
588 声望51 粉丝

下一步该干什么呢...