Skip to content

Instantly share code, notes, and snippets.

@Neo42
Last active January 7, 2022 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Neo42/2914d86f2b50272e98546994eb8f24bf to your computer and use it in GitHub Desktop.
Save Neo42/2914d86f2b50272e98546994eb8f24bf to your computer and use it in GitHub Desktop.
React Patterns: Compound Components
// Compound Components w/ `React.Children.map` & `React.cloneElement`
import * as React from 'react'
import {Switch} from '../switch'
function Toggle({children}) {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(!on)
return React.Children.map(children, child =>
// tell if a child is a built-in DOM component
typeof child.type === 'string'
? child
: React.cloneElement(child, {on, toggle}),
)
}
function ToggleOn({on, children}) {
return on ? children : null
}
function ToggleOff({on, children}) {
return on ? null : children
}
function ToggleButton({on, toggle, ...props}) {
return <Switch on={on} onClick={toggle} {...props} />
}
function App() {
return (
<div>
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleOff>The button is off</ToggleOff>
<span>Hello</span>
<ToggleButton />
</Toggle>
</div>
)
}
// ---------------------------------------------------------
// Flexible Compound Components w/ React Context (without location limits on children)
const ToggleContext = React.createContext()
function Toggle({children}) {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(!on)
// remember to forward `children` prop to render them
return <ToggleContext.Provider value={{on, toggle}} children={children} />
}
function ToggleOn({children}) {
const {on} = React.useContext(ToggleContext)
return on ? children : null
}
function ToggleOff({children}) {
const {on} = React.useContext(ToggleContext)
return on ? null : children
}
function ToggleButton(props) {
const {on, toggle} = React.useContext(ToggleContext)
return <Switch on={on} onClick={toggle} {...props} />
}
function App() {
return (
<div>
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleOff>The button is off</ToggleOff>
<div>
<ToggleButton /> // one level deeper, but still works because of Context
</div>
</Toggle>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment