Skip to content

Instantly share code, notes, and snippets.

@conormcafee
Last active October 9, 2020 08:08
Show Gist options
  • Save conormcafee/acbd8415018df43eebe2b01d97fe71d7 to your computer and use it in GitHub Desktop.
Save conormcafee/acbd8415018df43eebe2b01d97fe71d7 to your computer and use it in GitHub Desktop.
Example of how to use React.cloneElement
// Menu
/*
The benefit now with having the MenuItem controls confined to one component, we can
now wrap our "lists" in this MenuItem, and MenuItem will pass `isOpen` down to
BirthdayCardsList and BirthdayPresentsList without explicty stating that.
We could also pass `setIsOpen` too on MenuItem if there is an action within one of
our lists to trigger a close of the list.
*/
const Menu = () => {
return (
<>
<MenuItem text="Birthday Cards">
<BirthdayCardsList />
</MenuItem>
<MenuItem text="Birthday Presents">
<BirthdayPresentsList />
</MenuItem>
</>
)
}
// MenuItem.part
/*
MenuItem orginally was meant to contain a button, and when that
button gets clicked it then toggles a `nav` with further sub-navigation items.
However, when you need more than one of these on your menu, you begin to repeat
yourself with the local state, onClick handler and the useOutsideClick. Instead now,
MenuItem.part becomes a reuseable component, and thanks to React.cloneElement - enables us
to pass the isOpen prop down through its children without needing to explicity pass it around.
*/
const MenuItem = ({ text }) => {
const [isOpen, setIsOpen] = React.useState(false);
const ref = useRef();
// https://medium.com/@kevinfelisilda/click-outside-element-event-using-react-hooks-2c540814b661
useOutsideClick(ref, () => {
if (isOpen) {
setIsOpen(false);
}
});
return (
<div ref={ref}>
<button onClick={() => setIsOpen(!isOpen)}>
{text}
</button>
{React.cloneElement(children, {
isOpen,
})}
</>
)
}
// BirthdayCardsList
/*
Before using React.cloneElement - I would have had this complicated component
which contained the logic for hide show, the menu item button itself, and then
the real purpose of this component, a list of links.
Now it is a lot easier as it has all of the logic required within its parent
component (MenuItem). All `BirthdayCardList` has to worry about now is
rendering a list of links.
*/
const BirthdayCardsList = ({ isOpen }) => {
if (!isOpen) return null
return (
<nav>
<a href="#">Funny Cards</a>
<a href="#">Age Cards</a>
</nav>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment