I'm creating a popover component for a UI in React. That component contains a button that triggers the popover to display. Because the button needs to be configurable–label, classes, properties–I need to pass down these configuration parameters to the child. There are two main ways I see this happening.
- Pass down label and props as attributes:
<PopoverComponent buttonLabel={label} buttonProps={buttonProps} />
- Pass down the actual button.
const button = <Button>Show</Button>;
<PopoverComponent button={button} />
The complication comes inside the popover. In order to place the element I need a ref
to the DOM node, so I must add props to the button element inside the PopoverComponent
. In React this is straightforward and standard in case 1. Just spread any custom props to the button. E.g., <Button ref="popoverButton" onClick={this.onClick} {...extraProps }>
. However, in case 2, we need to React.cloneElement
and mutate the props. E.g.,
// INSIDE popoverComponent
const { button } = this.props;
const extendedButton = React.cloneElement(
button,
Object.assign(
{},
button.props, {
ref: "popoverButton",
onClick: this.onClick,
}),
);
return <div className="popover-control">{ extendedButton }</div>;
Is it antipattern to use React.cloneElement
and modify props in child components in React?
Best Answer
Adding additional props to a component is the purpose of React.cloneElement:
I typically use it in combination with other methods from React.Children API to encapsulate certain functionality in a parent container component. For example, a checked state management container for a sequence of any number of checkboxes:
You could do something similar. Have your parent PopoverComponent manage the opened state and use the Button component as a child. Whatever props relate just to the button declare on the button, and merge any props related to managing popover state when you clone it in the parent:
However, I think popovers are special cases because they should appear to render on top of the underlying HTML elements, so it's not a clear parent/child relationship. Also note
key
andref
are treated differently than other props when using React.cloneElement: