Reduce code redundancy with multiple buttons using React

Viewed 80

I have a series of icon buttons in which most properties are common. The only thing that changes is the icon type and onClick functions. Currently, I am implementing it in the following manner:

const listOfButtons =  <div>
            <IconButton
                flat
                icon={<AddIcon />}
                onClick={doSomething}
                primary
            />
            <IconButton
                flat
                icon={<EditIcon />}
                isDisabled
                primary
            />
            <IconButton
                flat
                icon={<TrashIcon />}
                isDisabled
                primary
            />
            <IconButton
                flat
                icon={<RefreshIcon />}
                primary
            />
            </div>;

I want to try to reduce the redundancy of the code and try to define only one IconButton object and pass different icons and different onClicks to it as in future there might be more icon buttons. I am stuck as to how to assign onclick to a particular icon button. Is there an efficient way of defining multiple icon buttons and assigning icons and onclicks than what I have done above? Please help!

4 Answers

You could create an array of the changing attributes and then use Array.map.

const buttonData = [
    {
        icon: <AddIcon />,
        onClick: doSomething
    },
    {
        icon: <EditIcon />,
        isDisabled: true
    },
    {
        icon: <TrashIcon />,
        isDisabled: true
    },
    {
        icon: <RefreshIcon />
    }
];

const listOfButtons =  <div>
    {
        buttonData.map((data, index) => (
            <IconButton
                key={index}
                flat
                icon={data.icon}
                onClick={data.onClick}
                isDisabled={data.isDisabled}
                primary
            />
        ))    
    }
</div>;

NOTE: When mapping React components React requires a key attribute on the root component returned from the map. This is so that React can keep track of which component is which (in the case of reordering or removing an item from the array). It is preferred to use a unique identifier from the item being mapped (as this will remain the same if the item changes position), but if you are in full control of the array in use and can ensure that no item ever changes index then using index is fine. If you use index and the items do change position it will result in more DOM changes than necessary.

Step 1: Create a separate component for button :

class IconButtons  extends React.Component{
render(){
return(
            <IconButton
                flat
                icon={this.props.icon}
                primary
                onClick = {this.props.onClick}
            />
)
}

Step 2 :

const buttonList = [
    {
        icon: <AddIcon />,
        onClick: doSomething
    },
    {
        icon: <EditIcon />,
        isDisabled: true
    },
    {
        icon: <TrashIcon />,
        isDisabled: true
    },
    {
        icon: <RefreshIcon />
    }
];
const listOfButtons =  
            <div>
              {
               buttonList .map((ele,index)=>(<IconButtons
                key=index
                icon={ele.icon}
                onClick={ele.onClick}
               />))
              }
            </div>;

Use Array.map to make it better

const iconsList = [<AddIcon />,<EditIcon />,<TrashIcon />,<RefreshIcon />]
const listOfButtons =  
            <div>
              {
               iconsList.map((icon,index)=>(<IconButton
                key=index
                flat
                icon={icon}
                onClick={doSomething}
                primary
               />))
              }
            </div>;

If I understand your question, you are looking for a DRYer solution. The solutions so far offer mapping from a config, which is certainly a viable option. One alternative option would be to use composition. A quick analysis of your list of buttons show the common props between them all are flat and primary; the remaining props can change with some variability. So you could define a <FlatPrimaryButton /> like so:

const FlatPrimaryButton = ({props}) => (
    <IconButton
        flat
        primary
        {...props}
    />
);

This would allow you to write something more like this for your list:

const listOfButtons = <div>
  <FlatPrimaryButton
    icon={<AddIcon />}
    onClick={doSomething}
  />
  <FlatPrimaryButton
    icon={<EditIcon />}
    isDisabled
  />
  <FlatPrimaryButton
    icon={<TrashIcon />}
    isDisabled
  />
  <FlatPrimaryButton
    icon={<RefreshIcon />}
  />
</div>;

This solution could then be combined with the other .map based solutions.

Related