Skip to content

Instantly share code, notes, and snippets.

@jamessawyer
Created March 13, 2024 02:41
Show Gist options
  • Save jamessawyer/4352b8088c0ccbdf63d36fac31604b45 to your computer and use it in GitHub Desktop.
Save jamessawyer/4352b8088c0ccbdf63d36fac31604b45 to your computer and use it in GitHub Desktop.
React Patterns
// Compound Components using Context
type AccordionContextProps = {
activeItemIndex: number
setActiveItemIndex: (index: number) => void
}
type AccordionItemProps = {
item: {
id: number
label: string
content: string
}
index: number
}
const AccordionContext = createContext<AccordionContextProps>({
activeItemIndex: 0,
setActiveItemIndex: () => 0
})
const Accordion = ({ children }: { children: ReactNode }) => {
const [activeItemIndex, setActiveItemIndex] = useState(0)
return (
<AccordionContext.Provider value={{ activeItemIndex, setActiveItemIndex }}>
<ul>
{children}
</ul>
</AccordionContext.Provider>
)
}
const AccordionItem = ({ item, index }: AccordionItemProps) => {
const { activeItemIndex, setActiveItemIndex } = useContext(AccordionContext)
return (
<li onClick={() => setActiveItemIndex(index)} key={item.id}>
<strong>{item.label}</strong>
{index === activeItemIndex && <p>{item.content}</p>}
</li>
)
}
function App() {
return (
<>
<Accordion>
<AccordionItem item={{ id: 1, label: 'item 1', content: 'content 1' }} index={0} />
<AccordionItem item={{ id: 2, label: 'item 2', content: 'content 2' }} index={1} />
<AccordionItem item={{ id: 3, label: 'item 3', content: 'content 3' }} index={2} />
</Accordion>
<main>
<p>main app here</p>
</main>
</>
)
}
// Props Collection with prop getter
// 通过组合的方式对传入的属性进行扩展
const compose = (...functions: Function[]) => {
return (...args: any[]) => {
return functions.forEach(fn => fn?.apply(...args))
}
}
const getDroppableProps = ({
onDragOver: replacementOnDragOver,
...replacementProps
}) => {
const defaultOnDragOver = (e: Event) => {
e.preventDefault()
}
// 对onDragOver添加一些自定义逻辑
return {
onDragOver: compose(replacementOnDragOver, defaultOnDragOver),
onDrop: (e: Event) => {},
...replacementProps
}
}
function App() {
return (
<>
<Dropzone {
...getDroppableProps({
onDragOver: () => {
alert('Draged!')
}
})
}
/>
<main>
<p>main app here</p>
</main>
</>
)
}
// Render Props Pattern
type WindowSizeProps = {
render: (size: { width: number, height: number }) => JSX.Element
}
const WindowSize = (props: WindowSizeProps) => {
const [size, setSize] = useState({ width: -1, height: -1 })
useEffect(() => {
const handleSize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight })
}
window.addEventListener('resize', handleSize)
return () => {
window.removeEventListener('resize', handleSize)
}
}, [])
return props.render(size)
}
function App() {
return (
<>
<WindowSize render={({ width, height }) => <div>width: {width}, height: {height}</div>} />
<main>
<p>main app here</p>
</main>
</>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment