当前位置:网站首页>[novice] develop a tab component that is easy to expand by hand
[novice] develop a tab component that is easy to expand by hand
2022-07-24 03:48:00 【Oriental pajamas】
In the daily business development process , I found that there are many excellent component design patterns , Making good use of these patterns can effectively improve the maintainability of the code , But often a lot “ Novice ” Developers are not yet proficient , It makes the written code difficult to extend . This article will share from scratch how to develop an extensible Tabs Components .
The following content will explain the knowledge points used in as much detail as possible , But the point of this article is not Tabs Component implementation .
Don't talk nonsense , Look at the effect :

On-line codesandbox:codesandbox.io/s/jolly-fro…
The effect is simple , Click one at a time Tab Elements , Switch the content area to show the corresponding content . There are many similar scenes , Sometimes it's just the skin , But the implementation logic and effect are the same . for example Antd Medium Tabs Components :

Don't think about cut scenes 、 Compatible with scrolling 、 Dynamic increase / decrease scenarios , One
TabsComponents are actually very simple , I believe most people will be able to do it soon .
trap
First , from UI Think about component splitting from the perspective of , You can see clearly that every Tab Item is duplicate , They all have an icon 、 A name 、 Two display states , Respond to click events, etc .

Design according to this idea Tab Components , The code is like this :
interface TabProps extends HTMLAttributes<HTMLElement> {active: boolean;name: string;iconType: Parameters<typeof Icon>["0"]["type"];
}
function Tab(props: TabProps) {const { active, name, iconType, className, ...rest } = props;return (<liclassName={clsx("tab", className, {active: active})}{...rest}><Icon type={iconType} className="icon" />{name}</li>);
}
We let TabProps Inherited from HTMLAttributes<HTMLElement>, So universal props for example style/className/onClick You can omit . stay render Also directly through the expansion operator (...rest) Just pass it through .
Propsactive Attributes are used to distinguish Tab State of component ;name It's the name of the show ;iconType Used to distinguish Tab The component uses Icon type , It will serve as type Pass to Icon Components .
there
IconIt is a common component , In business development, it may correspond to icon components in a component library , In the example, I use svgr To introduce svg file , By transmission type Type to distinguish the icons used .const icons = { homeOutline: Home, musicOutline: Music, personOutline: Person, searchOutline: Search } as const; export function Icon(props: Props) { const { type, ...rest } = props; let Component = icons[type]; return <Component {...rest} />; }
Parameters<typeof Icon>["0"]["type"]; Its function is to take Icon In the first parameter of the method type type Field
next , Will be multiple Tab The components are packaged as Tabs Components , Tell it what it has Tab, Every Tab The name of the component and the icon are OK .
interface TabsProps {tab: string;tabs: Omit<TabProps, "active">[];onChange: (val: string) => void;
}
function Tabs(props: TabsProps) {const { tab, tabs, onChange } = props;return (<ulclassName="tabs"style={
{"--tab-total": tabs.length,"--tab-active-index": tabs.map((item) => item.name).indexOf(tab)} as React.CSSProperties}>{tabs.map(({ name, iconType }) => {return (<Tabname={name}active={tab === name}iconType={iconType}onClick={() => onChange(name)}/>);})}</ul>);
}
stay .tabs Element CSS Variables, combination CSS Linkage to control the position of the indicator .
"--tab-total": 4,
"--tab-active-index": ["Home", "Music", "Search", "Person"].indexOf(tab)
The premise of this is that every Tab When highlighted , The width of the indicator is fixed , If you need to dynamically change the size of the indicator , Then you also need to dynamically get the current Tab Width .
.tabs {&::before {left: calc((var(--tab-active-index) + 0.5) / (var(--tab-total)) * 100%);}
}
In this way, we have encapsulated a Tabs Components , When in actual use, it will be combined with state Connect , according to tab To determine which content area to display , The code is about as long as :
funciont Basic () {const [tab, setTab] = useState("Home");const tabs = [{ name: "Home", iconType: "homeOutline" },{ name: "Music", iconType: "musicOutline" },{ name: "Search", iconType: "searchOutline" },{ name: "Person", iconType: "personOutline" }]return (<div>{tab === "Home" ? <div className="tab-pane">Home</div> : null}{tab === "Music" ? <div className="tab-pane">Music</div> : null}{tab === "Search" ? <div className="tab-pane">Search</div> : null}{tab === "Person" ? <div className="tab-pane">Person</div> : null}<Tabstab={tab}onChange={setTab}tabs={tabs}/></div>)
}
The complete code is here :codesandbox.io/s/jolly-fro…
Because the probability of each content area is different , You don't need to package it into components .
I wonder if you are familiar with this code structure ? It looks like a good package , In fact, the scalability is very poor , Let's take a look at the problems with this component .
problem
There is no problem with such code being developed only once , Be careful if you continue iterations later , It can cause code to get out of control . I have seen too many similar scenes , Due to poor component scalability , Afraid to change the outlet line , So some developers will choose to copy one to support new features .
Take particles to illustrate , The following scenarios need to be supported elsewhere in the project :
Show only icons 
Show name only 
In the individual Tab Add small dots on the element 
Add some active corner markers to a copy 
The first two are OK , Just modify TabProps take iconType and name Change the attribute to non mandatory , If you don't pass it on, you won't show it , For spatial balance , There may be some work to be done padding/margin Minor adjustment of .
In the third and fourth cases, you have to add fields …
However , As feature requirements grow , Various fields are also increasing , Finally, you will find this Tabs The components are huge , It's hard to maintain . I don't know if some historical fields are still in use , Dare not delete ; Field A and Field B There are style conflicts , You can't set... At the same time ; Field C And field D Strong binding , Yes C You have to pass D And so on . obviously Tabs Components are transiently encapsulated , Ability is quite limited .
broken
According to the principle of open and closed , We can Tabs The general ability to extract and package , And all kinds of different displays retain the open ability .Tabs What are the capabilities of components ? In fact, it is highlighted 、 Click event 、 And switching the display content , This part is general , No matter what UI You have the ability to look like this . And each Tab The content of is changing , There are different display requirements in different scenarios .
Based on this idea , Can be UI The rendered part is completely handed over to the specific scene ,Tabs Components retain extensibility , stay React in , have access to children Or by props Pass on ReactElement To support ” slot “.
Modify the code ,Tab Components no longer care about icons and names , Direct Render children got .
interface TabProps extends HTMLAttributes<HTMLElement> {active?: boolean;pane?: ReactElement
}
function Tab(props: TabProps) {const { active, className, children, ...rest } = props;return (<liclassName={clsx("tab", className, {active: active})}{...rest}>{children}</li>);
}
Added pane attribute ( Optional ), Just for props The type definition uses , The real consumers are Tabs.
function Tabs( props: PropsWithChildren<{value: string;onChange: (value: string) => void;}> ) {const { value, children, onChange } = props;const tabValues =Children.map(children, (item: any) => item?.key || "") || [];return (<>{Children.map(children, (item: any) =>item.key === value ? item.props.pane : null)}<ulclassName="tabs"style={
{"--tab-total": tabValues.length,"--tab-active-index": tabValues.indexOf(value)} as React.CSSProperties}>{Children.map(children, (item: any) =>React.cloneElement(item, {active: item.key === value,pane: null,onClick: () => onChange(item.key)}))}</ul></>);
}
Tabs Components also support children slot , Automatically from children Extract all Tab Value, The component is the configuration , No longer need tabs attribute .
stay React in
keyIt's special prop, It will not appear in the component's props Inside , Instead, it is extracted and assigned to ReactElement.
And the function of switching the content area is also incorporated ( It's for Tab Of pane attribute ), Only with the currently active tab Content will be displayed .
{Children.map(children, (item: any) => item.key === value ? item.props.pane : null
)}
Event binding and active States can also be cohesive , adopt React The top-level approach cloneElement take element Let's do it again , Pass in an additional... In the second parameter props, complete active State judgment and click event binding .
{Children.map(children, (item: any) =>React.cloneElement(item, {active: item.key === value,pane: null,onClick: () => onChange(item.key)})
)}
End use , The code is as follows :
export function Advance() {const [tab, setTab] = useState("Home");return (<div className="page"><Tabs value={tab} onChange={setTab}><Tabkey="Home"pane={<div className="tab-pane">Home</div>}><Icon type="homeOutline" className="icon" />Home</Tab><Tabkey="Music"pane={<div className="tab-pane">Music</div>}><Icon type="musicOutline" className="icon" />Music</Tab>{...}</Tabs></div>);
}
You can see , After adjustment ,Tab Components become more pure , No longer need to be passed on as a tool person iconType, And no longer limit specific UI The representation , Support for rendering arbitrary content , Now you can support more usage scenarios .
边栏推荐
- 。其中调用时传入t指与Devi遍的,根本问t2,
- Bet on the whole scene, what is the odds of glory?
- DOM related method concepts
- Paper reading: the perfect match: 3D point cloud matching with smoothed densities
- SLAAC stateless address automatic configuration
- Technical dry goods | how difficult is data processing? Take a look at the solution provided by mindspire!
- 。 When calling, the incoming t refers to the Devi pass, which basically asks T2,
- MySQL message queue list structure
- IO流分类整理
- Yu zhirs] below refers to the return structure push sent to the remote terminal
猜你喜欢

MySQL学习——MySQL软件的安装及环境配置(Windows)详细!

Worthington purified enzyme preparation helps neonatal cardiomyocyte isolation system
![Scenario and value of data desensitization [summary]](/img/15/ebfbbb708c94417e7291941e76b3a3.png)
Scenario and value of data desensitization [summary]

Mitsubishi Ethernet module Yuanchuang intelligent control yc8000-fx connection MCGS operation method

MLP - Multilayer Perceptron

Two stroke engine mean value model simulation

Qt ROS相关操作(运行终端指令、发布订阅自定义消息话题或服务、订阅图像并显示)

MySQL message queue list structure

6-13 vulnerability exploitation -smtp brute force cracking
![Algorithm interview high frequency problem solving guide [1]](/img/a9/bca12e937526cf55bbea575d402e50.png)
Algorithm interview high frequency problem solving guide [1]
随机推荐
[super complete sorting] Cisco and Huawei order comparison memo, take it away without thanks! Anytime, anywhere
Problem solution of supporting problem solution of heavy chain dissection example
Worthington purified enzyme preparation helps neonatal cardiomyocyte isolation system
清单的要快速熟悉并掌昇腾AI处理器支持
Pat class a 1040 long symmetric string
Standard C language 10
2022 China software products national tour exhibition is about to set sail
[JS reverse hundred examples] a public resource trading network, reverse analysis of announcement URL parameters
RTOS内功修炼记(十) | 深度解析RTOS内核上下文切换机制
[interpolation expression of applet, rendering judgment, binding events and sharing]
Android开发——Kotlin语法之Lambda表达式
Paper reading: the perfect match: 3D point cloud matching with smoothed densities
于最rs]在下面指发送到远程大的返回结构推境
IO stream sorting
Do you know how to do interface testing well?
Yu zhirs] below refers to the return structure push sent to the remote terminal
Demining game (analysis)
SqlServer 备份还原
Shengsi YiDianTong | deep learning analysis of classical convolutional neural network
Pat grade a 1043 is it a binary search tree