[英]Is there any practical way to call `React.createContext()` within a component?
Let's say I want to create a UI component for an "accordion" (a set of collapsible panels).假设我想为“手风琴”(一组可折叠面板)创建一个 UI 组件。 The parent component controls the state of which panels are open, while the child panels should be able to read the context to determine whether or not they're open.父组件控制哪些面板打开的状态,而子面板应该能够读取上下文以确定它们是否打开。
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}
</>
)
}
The only way I could think of accomplishing this would be to have Accordion
run an effect whenever children
changes, then traverse children
deeply and find AccordionSection
components, while not recursing any nested Accordion
components -- then cloneElement()
and inject context
as a prop to each AccordionSection
.我能想到的唯一方法是让Accordion
在children
发生变化时运行效果,然后深入遍历children
并找到AccordionSection
组件,同时不递归任何嵌套的Accordion
组件——然后cloneElement()
并注入context
作为道具每个AccordionSection
。
This seems not only inefficient, but I'm not even entirely sure it will work.这似乎不仅效率低下,而且我什至不完全确定它会起作用。 It depends on children
being fully hydrated when the effect runs, which I'm not sure if that happens, and it also requires that Accordion
's renderer gets called whenever deep children change, which I'm not sure of either.这取决于效果运行时children
是否完全水合,我不确定是否会发生这种情况,并且它还要求Accordion
的渲染器在深度孩子发生变化时被调用,我也不确定。
My current method is to create a custom hook for the developer implementing the Accordion.我目前的方法是为实现手风琴的开发人员创建一个自定义挂钩。 The hook returns a function which returns the isOpen
and onToggle
functions which have to manually be passed to each rendered AccordionSection
.该钩子返回一个函数,该isOpen
返回必须手动传递给每个渲染的AccordionSection
的isOpen
和onToggle
函数。 It works and is possibly more elegant than the children solution, but requires more overhead as the developer needs to use a hook just to maintain what would otherwise be state encapsulated in Accordion
.它有效并且可能比 children 解决方案更优雅,但需要更多的开销,因为开发人员需要使用钩子来维护原本封装在Accordion
状态。
React.createContext
will return an object that holds 2 components: React.createContext
将返回一个包含 2 个组件的对象:
These 2 components can share data, the Consumer
can "grab" the context data from the nearest Provider
up the tree (or use the useContext
hook instead of rendering a Consumer
).这两个组件可以共享数据, Consumer
可以从最近的Provider
“抓取”树上的上下文数据(或使用useContext
钩子而不是呈现Consumer
)。
You should create the context object outside the parent component and use it to render a Consumer
inside your children
components (or use the useContext
hook).您应该在父组件外部创建上下文对象,并使用它在children
组件内部呈现Consumer
(或使用useContext
钩子)。
Simple example:简单的例子:
const myContext = createContext();
const Accordion = ({ children }) => {
// ...
return (
<myContext.Provider value={...} >
{children}
</myContext.Provider>
)
}
const AccordionSection = (...) => {
const contextData = useContext(myContext);
// use the data of your context as you wish
// ...
}
Note that i used the useContext hook instead of rendering the Consumer
, its up to you if you want to use the hook or the Consumer
.请注意,我用的useContext ,而不是渲染钩Consumer
,它取决于你,如果你想使用挂钩,或Consumer
。
You can see more examples and get more details from the docs您可以查看更多示例并从文档中获取更多详细信息
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.