如何将一组React节点传递给不相关的组件

时间:2020-03-26 12:45:35

标签: reactjs react-router react-dom

我对React有一个疑问,这是React应用的简化版本。

在应用中,我想呈现一个固定的主菜单和一个可选的次菜单,其内容由路由中呈现的内部组件控制。

辅助菜单也在应用程序的移动版本中的其他位置呈现。

function App() {
    return <Router>
        <PrimaryMenu/>

        <SecondaryMenu/>

        <LayoutContent/>
        {/* This block is rendered only on mobile devices */}
        <Responsive  {...Responsive.onlyMobile}>
            <SecondaryMenu/>
        </Responsive>
    </Router>;
}

LayoutContent将根据路由规则呈现实际的页面内容(使用Page组件,并且每个页面组件都可以像这样呈现其自己的辅助菜单(例如page1具有其自己的子菜单,page2具有另一个子菜单,page3具有

<Page title='Page 1 - With secondary menu'>
    <SecondaryMenuItems>
        {/* I want this content as children of secondary menu in both mobile and desktop menubars */}
        <li>Page 1 item 1</li>
        <li>Page 1 item 2</li>
    </SecondaryMenuItems>
</Page>

我试图通过使用React Contexts实现它,但是如果我将子节点存储在上下文中,则会触发无限渲染。我将其更改为在<SecondaryMenuItems/>组件中使用id属性,但是这种方法非常脆弱,并且也有一些缺点。

这是我的working example,它可以正常工作,但是正如我所说的那样,它很脆弱:

  • 如果我在辅助菜单中使用重复的ID,该怎么办?
  • 如果我忘记了二级菜单键怎么办?

如果您切换到带有菜单的页面,然后转到第3页(没有菜单),则上一页菜单仍保留在屏幕上。

如何通过反应来做到这一点?有建议的方法吗?

表达我的问题的一种更简单的方法是“如何在不相关的组件(例如同级组件)之间传递一组反应节点”

更新

我已经通过收到提示来完成了我的工作示例,现在通过将useRefReactDOM.createPortal组合在一起,我获得了最终的结果,该结果已在示例中显示。

2 个答案:

答案 0 :(得分:1)

这是React Portals的用例。门户网站可让您将页面中的辅助菜单项呈现到其他地方存在的辅助菜单容器中

您需要做的就是在页面渲染中调用React.createPortal,将渲染的元素和目标节点传递到其中,而不管其在DOM树中的位置如何

我已经在https://codesandbox.io/s/secondary-menu-example-vbm3x的门户网站上编辑了您的示例。当然,这是一个基本示例,为了方便起见,您可能希望将门户网站逻辑抽象到单独的组件中,并且/或者从父级传递dom引用,而不是在mount上调用getElementById

答案 1 :(得分:0)

在多个同级节点中渲染相同的子级

问题询问“如何传递一组反应节点”。理想情况下,不要这样做。如果要在层次结构中的某个位置渲染节点,而又打算在其他地方使用它们,则可能使用了错误的策略。

如果需要在不同的位置渲染相同的组件,请创建一个函数来渲染这些组件,然后从两个位置调用它。换句话说, 总是传递信息,而不传递呈现的元素

在路由器内部渲染

在典型的单页应用程序中,路由器将呈现所有(非静态)组件。这是示例应做的方式。路由组件(LayoutContent)应该直接负责渲染“通过的节点”(SecondaryMenu)。

<Route path="/page1">
  <Page title="Page 1 - With secondary menu">
    <SecondaryMenu id="menu1"> {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
      <li>Page 1 item 1</li>
      <li>Page 1 item 2</li>
    </SecondaryMenu>           {/* <- use SecondaryMenu instead of SecondaryMenuItems */}
  </Page>
</Route>

在路由器内部无法渲染时

如果由于某种原因路由组件无法直接呈现内容,则单页应用程序(或路由)解决方案可能是错误的解决方案。该问题不包含有关为何无法在路由器内部渲染组件的任何信息,请随时编辑问题并提供更多信息。

实现该示例的另一种方法是不存在路由组件(即没有LayoutContent),并且让SecondaryMenu检查页面的路径并根据该条件有条件地呈现适当的内容。 当有路由器组件为您完成此任务时,根据路径手动有条件地渲染可能很愚蠢,我同意。解决方案是根本不使用路由器。试图在路由器中渲染孩子并通过它们会产生强烈的代码异味。

在React层次结构布局中,如果需要相同的信息,则决定在多个位置(在这种情况下为路径)进行渲染,将该信息上移到所有组件的最近父级,并将其作为prop或作为上下文传递。

避免ID冲突

“如果我在辅助菜单中使用重复的ID,该怎么办?”

如果您调用一个函数来呈现辅助菜单而不是呈现并传递辅助菜单,则可以在props中传递菜单前缀,并在函数中使用此菜单前缀。

function SecondaryMenuItems({ children, idPrefix, path }) {
  if (path == '/path1') {
    return (
      <ul id={`${idPrefix}-newlist`}>

打开键

“如果我忘记了二级菜单键怎么办?”

反应键只需要唯一的within a rendered list。实际上,键只是一种优化,可以防止React在每次通过时重新渲染生成的列表。如果您忘记包含一个密钥(或对密钥进行了错误的选择),React每次都必须重新渲染该列表,但这并不比这更严重。在简单的情况下,您不会注意到性能下降。