React Refactoring - Composition: When don't use it!
Tags: react, refactoring, webdev, beginners
Published: 2022-11-29 01:22:53

## Intro
I refact a dropdown composition component. Changing it to object.

We had a dropdown in the following structure:

``` jsx
<Dropdown>
   <Dropdown.Toggle color='primary' />
   <Dropdown.Menu>            
      <Dropdown.Item onClick={() => {}}>
         MyLabel
      </Dropdown.Item>
      <Dropdown.Item onClick={() => {}}>
         AnotherLabel
      </Dropdown.Item>
   </Dropdown.Menu>
</Dropdown>
```

## Problem
When we need to hide some elements, we need to apply conditional rendering and with that, we have a mixture of javascript with jsx syntax that makes the code difficult to read and difficult to maintain. For example: 

``` jsx
<Page>
   <Subheader helpText="Choose a Publication action">
      <Dropdown>
         <Dropdown.Toggle color='primary' />
         <Dropdown.Menu>
            {allowEdit ? 
               (                  
                  <Dropdown.Item onClick={handleTransfer}>
                     Transfer
                  </Dropdown.Item>
                  <Dropdown.Item onClick={handleAbandon}>
                     Abandon
                  </Dropdown.Item>
               ) : (                        
                     {hasPermission('Pages.Publications.Publish') ? (                     
                        <Dropdown.Item onClick={handlePublish}>
                           Publish
                        </Dropdown.Item> 
                     ) : (
                        <Dropdown.Item onClick={handleTransfer}>
                           Transfer
                        </Dropdown.Item>
                     )}
                  )
               )}
         </Dropdown.Menu>
      </Dropdown>            
   </Subheader>
   <Content>
   ...
   </Content>
<Page>
```

Realize that there is a ternary inside the other, and also, there is a code duplication for the 'Transfer' item. This is terrible to read.

If **allowEdit** is TRUE, it should render the 'Transfer' and 'Abandon' items:

![transfer and abandon items](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ds01p7ngsp2fw3tf6anl.png)

If **allowEdit** is FALSE and user has permission to Publish, it should render only 'Publish' item. 
Or 'Transfer' for fallback.

![just publish item](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwqc1hqlvkslhte1iczf.png)

![transfer as fallback item](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1msvxmph6gnpvmpnm82q.png)

## Proposal
Create a new component (**DropdownButton**) to support options that receive list items instead of children. Like this:

``` jsx
<DropdownButton color='primary' options={[
   { label: '', onClick: () => {}, visible: true }
   { label: '', onClick: () => {}, visible: true }
  ]}
/>	
```
The **visible** property should control when showing the dropdown item.

## Benefits
- The code is easier to read
- Less code repetition
- We can import the object from elsewhere

And applying our conditional rendering...

``` tsx
<Page>
   <Subheader helpText="Choose a Publication action">   
   <DropdownButton
      color='primary'
      options={[
         {
            label: 'Transfer',
            onClick: handleTransfer,
            visible: allowEdit || !hasPermission('Pages.Publications.Publish')
         },
         {
            label: 'Abandon',
            onClick: handleAbandon,
            visible: allowEdit
         },
         {
            label: 'Publish',
            onClick: handlePublish,
            visible: !allowEdit && hasPermission('Pages.Publications.Publish')
         },
      ]}
   />
   </Subheader>
   <Content>
   ...
   </Content>
<Page>
```

## Conclusion
We are used to writing more declarative code, always thinking about generic code, but sometimes it makes our code difficult to read and maintain.
Props as object items are widely used and we can use them when we want to better control our components. In this refactoring, we've assigned responsibility for the conditional rendering requirements to the **visible** prop.