Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Decompose React children to improve DX
Kezios profile imageAlexandre Fauchard
Alexandre Fauchard forKezios

Posted on • Edited on • Originally published atkezios.fr

     

Decompose React children to improve DX

Today I needed to make a tab system.
Perfect for display multiple types of data in a small space, a tab system has two parts :

  • The header always display all the tabs labels
  • The content part display the data associated to the selected tab

The complexity of this kind of system is that we have a fixed part and a dynamic part, let's see two implementations.

V1 – Simple to code, hard to use

A first idea is to do a simple component with atabs prop corresponding to an array of objects with alabel and acontent which can be called like this :

<TabViewtabs={[{label:"First tab",content:<p>My first tab content</p>},{label:"Second tab",content:<p>My second tab content</p>},{label:"Third tab",content:<p>My third tab content</p>}]}/>
Enter fullscreen modeExit fullscreen mode

I could put content into variable, but it's for the example
The corresponding<TabView> component should look like this :

constTabView=({tabs})=>{const[selectedTabIndex,setSelectedTabIndex]=useState(0)return(<div><divclassName="header">{tabs.map(tab=>(<p>{tab.label}</p>))}</div><divclassName="content">{tabs[selectedTabIndex].content}</div></div>)}
Enter fullscreen modeExit fullscreen mode

First problem, I need a conditional tab and with this configuration it's complicated 😕
We have to put the tabs into a variable and add an optional tab if necessary... Something like that :

constdisplayThirdTab=...consttabs=[{label:"First tab",content:<p>My first tab content</p>},{label:"Second tab",content:<p>My second tab content</p>}]if(displayThirdTab){tabs.push({label:"Third tab",content:<p>My third tab content</p>})}return(<TabViewtabs={tabs}/>)
Enter fullscreen modeExit fullscreen mode

It's starting to get complicated to use, and we can do better. If we change my<TabView> component, we can make a moredev-friendly component which is used like that :

<TabView><Tablabel="First tab"><p>My first tab content</p></Tab><Tablabel="Second tab"><p>My second tab content</p></Tab>{displayThirdTab&&(<Tablabel="Third tab"><p>My third tab content</p></Tab>)}</TabView>
Enter fullscreen modeExit fullscreen mode

V2 – Not so difficult to code, much easier to use

The difficulty with the above component lies in the fixed part. We need to display only a part of the children.

To do this, we start by creating a "ghost-component" called<Tab> which will render nothing

constTab=({tabs})=>{//Rendered in TabView componentreturnnull}
Enter fullscreen modeExit fullscreen mode

With typescript, we can specify the props we need to use them in<TabView>

Then, we will write the base of the<TabView> component.

constTabView=({children})=>{const[selectedTabIndex,setSelectedTabIndex]=useState(0)consttabsInfo=[]consttabsContent=[]//TODO : Parse childrenreturn(<div><divclassName="header">{tabsInfo.map(({label})=>(<p>{label}</p>))}</div><divclassName="content">{tabsContent[selectedTabIndex]}</div></div>)}
Enter fullscreen modeExit fullscreen mode

You can see two arrays :

  • tabsInfo will contain all the tabs headers data (just a label in our case)
  • tabsContent will contain all the<Tab> componentschildren props

We now need to parse thechildren prop to fill our arrays.
To do this, we add a function calledparseTab

constparseTab=(node)=>{//We extract children from the <Tab> propstabsContents.push(node.props.children)//We extract label from <Tab> propstabsInfo.push({label:node.props.label})}
Enter fullscreen modeExit fullscreen mode

We just have to call it for each node in children with theReact.Children.map

React.Children.map(children,parseTab)
Enter fullscreen modeExit fullscreen mode

Here we are, our final<TabView> component

constTabView=({children})=>{const[selectedTabIndex,setSelectedTabIndex]=useState(0)consttabsInfo=[]consttabsContent=[]constparseTab=(node)=>{//We extract children from the <Tab> propstabsContents.push(node.props.children)//We extract label from <Tab> propstabsInfo.push({label:node.props.label})}React.Children.map(children,parseTab)return(<div><divclassName="header">{tabsInfo.map(({label})=>(<p>{label}</p>))}</div><divclassName="content">{tabsContent[selectedTabIndex]}</div></div>)}
Enter fullscreen modeExit fullscreen mode

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Il n'y a pas de bons services sans bonnes technologies

More fromKezios

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp