有什么实用的方法可以在组件中调用 `React.createContext()` 吗?

M M*_*ler 5 javascript reactjs react-context

假设我想为“手风琴”(一组可折叠面板)创建一个 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}
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)

我能想到的唯一方法是在Accordion每次children更改时运行效果,然后children深入遍历并查找AccordionSection组件,同时不递归任何嵌套Accordion组件 - 然后作为 propcloneElement()注入context每个AccordionSection.

这似乎不仅效率低下,而且我什至不完全确定它会起作用。这取决于children效果运行时是否完全水合,我不确定是否会发生这种情况,并且还需要Accordion在深子级发生变化时调用 's 的渲染器,我也不确定。

我目前的方法是为实现手风琴的开发人员创建一个自定义挂钩。钩子返回一个函数,该函数返回isOpenonToggle必须手动传递给每个渲染的函数AccordionSection。它有效并且可能比 children 解决方案更优雅,但需要更多的开销,因为开发人员需要使用钩子来维护否则将被封装在Accordion.

Sag*_*b.g 5

React.createContext将返回一个包含 2 个组件的对象:

  1. 提供者
  2. 消费者

这两个组件可以共享数据,可以从树上Consumer最近的位置“抓取”上下文数据(或使用钩子而不是渲染)。ProvideruseContextConsumer

您应该在父组件外部创建上下文对象,并使用它来渲染组件Consumer内部children(或使用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
  // ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,我使用了useContext挂钩而不是渲染Consumer,这取决于您是否想使用该挂钩或Consumer.

您可以查看更多示例并从文档中获取更多详细信息

  • 谢谢你,我错过了一些完全明显的东西!我的印象是“createContext()”创建了一个包含单一上下文值的单一容器。直到现在我才意识到每个*Provider*都有自己的价值;因此,多个手风琴将使用相同的“createContext()”对象,但每个手风琴都会有自己的上下文数据,只要每个都包装在自己的提供者中。这很有道理,并使这种“正常”方式成为可能。谢谢! (6认同)