Допустим, я хочу создать UI-компонент для "гармошки" (набора складных панелей). Родительский компонент управляет состоянием открытых панелей, в то время как дочерние панели должны иметь возможность считывать контекст, чтобы определить, открыты они или нет.
const Accordion = ({ children }) => {
const [openSections, setOpenSections] = useState({})
const isOpen = sectionId => Boolean(openSections[sectionId])
const onToggle = sectionId => () =>
setOpenSections({ ...openSections, [sectionId]: !openSections[sectionId] })
const context = useMemo(() => createContext(), [])
// Can't tell children to use *this* context
return (
<context.Provider value={useMemo(() => ({ isOpen, onToggle }), [isOpen, onToggle])}>
{children}
</context.Provider>
)
}
const AccordionSection = ({ sectionId, title, children }) => {
const { isOpen, onToggle } = useContext(context)
// No way to infer the right context
return (
<>
<button onClick={onToggle(sectionId)}>{isOpen(sectionId) ? 'Close' : 'Open'}</button>
{isOpen && children}
</>
)
}
Единственный способ, которым я мог думать о выполнении Accordion
будет запускать эффект всякий раз, когда изменяется children
, затем проходить глубоко children
и находить AccordionSection
компонентов, не возвращая при этом никаких вложенных Accordion
компонентов - затем cloneElement()
и вводить context
как опора для каждого AccordionSection
.
Это кажется не только неэффективным, но я даже не совсем уверен, что это будет работать. Это зависит от того, будет ли children
полностью увлажненным при запуске эффекта, что я не уверен, если это произойдет, и это также требует, чтобы средство визуализации Accordion
вызывалось всякий раз, когда изменяются глубокие дети, в чем я не уверен .
Мой текущий метод заключается в создании настраиваемого хука для разработчика, реализующего Аккордеон. Хук возвращает функцию, которая возвращает функции isOpen
и onToggle
, которые должны быть вручную переданы каждому отображаемому AccordionSection
. Он работает и, возможно, более элегантен, чем дочернее решение, но требует больше накладных расходов, поскольку разработчику необходимо использовать ловушку только для того, чтобы поддерживать то, что в ином случае было бы инкапсулировано в Accordion
.