有啥实用的方法可以在组件中调用“React.createContext()”吗?
Posted
技术标签:
【中文标题】有啥实用的方法可以在组件中调用“React.createContext()”吗?【英文标题】:Is there any practical way to call `React.createContext()` within a component?有什么实用的方法可以在组件中调用“React.createContext()”吗? 【发布时间】:2020-05-16 09:13:40 【问题描述】:假设我想为“手风琴”(一组可折叠面板)创建一个 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
的渲染器,这我也不确定.
我目前的方法是为实现 Accordion 的开发人员创建一个自定义挂钩。该钩子返回一个函数,该函数返回必须手动传递给每个渲染的AccordionSection
的isOpen
和onToggle
函数。它可以工作并且可能比儿童解决方案更优雅,但需要更多开销,因为开发人员需要使用挂钩来维护原本封装在 Accordion
中的状态。
【问题讨论】:
不知道为什么要在父级而不是外部创建上下文...(或者为什么用useMemo
包装它)
我同意@Sagivb.g。为什么要将其保留在组件内?把它移到外面,导出它,然后由其他组件导入它
context 不仅适用于父母对孩子,还可以在任何地方使用。只需将其视为状态包装器。
关键是没有什么是全球独一无二的,就像每个 Accordion 都保持自己的状态,哪些部分是打开的。手风琴的多个实例需要它们自己的上下文。但是 React 上下文的本质是,当“根”组件被渲染时,你不能即时实例化新的上下文实例。
我认为这不是在 React 中可以实际完成的事情。例如,如果您想要在应用程序中使用更多的 Redux 存储,Redux 要求您在从非默认存储读取的每个连接组件上指定存储的名称。所以我认为需要相同的方法,即如果一个组件服务于多个上下文,则子组件需要提供唯一标识符,而不仅仅是对其最近的父提供者的上下文进行魔法访问。在这里做了一个 POC:codesandbox.io/s/sweet-hofstadter-b42iy
【参考方案1】:
React.createContext
将返回一个包含 2 个组件的对象:
-
提供者
消费者
这两个组件可以共享数据,Consumer
可以从树上最近的Provider
“抓取”上下文数据(或使用useContext
挂钩而不是渲染Consumer
)。
您应该在父组件之外创建上下文对象,并使用它在您的children
组件内呈现Consumer
(或使用useContext
挂钩)。
简单示例:
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
// ...
请注意,我使用了useContext 钩子而不是渲染Consumer
,如果你想使用钩子或Consumer
,这取决于你。
您可以通过docs查看更多示例并获取更多详细信息
【讨论】:
谢谢,我遗漏了一些非常明显的东西!我的印象是createContext()
创建了一个包含奇异上下文值的奇异容器。直到刚才我才意识到每个 Provider 都有自己的价值;因此,多个手风琴将使用相同的createContext()
对象,但只要每个手风琴都包装在自己的提供程序中,每个手风琴都会有自己的上下文数据。这很有意义,并使这成为“正常”方式成为可能。谢谢!以上是关于有啥实用的方法可以在组件中调用“React.createContext()”吗?的主要内容,如果未能解决你的问题,请参考以下文章
Angular 2+,有啥方法可以声明一个模块中的所有组件,然后在 app.module.ts 中导入?