即时创建React上下文菜单

4

问题

是否有一种方法可以动态创建React组件(或者更具体地说:上下文菜单),即仅在用户请求此上下文菜单时才创建?

构建个别上下文菜单所需的所有信息已经存在于触发它的组件上,这意味着不需要等待任何异步获取数据。

在其他情况下,这很容易实现,但我无法理解如何在React+Redux应用程序中完成此操作。


在撰写此文时,唯一想到的是在触发上下文菜单时发送一个动作,并将此单个上下文菜单包含在生成的重新呈现中作为某种弹出对话框。这样至少可以避免过早创建大量不可见组件。但这种方法似乎有些反模式。我在这里错过了什么?

背景

在我的React应用程序中显示的状态以分层结构呈现,实际上嵌套了六到七个级别。

在顶层,它是一个简单的列表。但每个列表项都有以下结构:

Item = {
    priorChildren: List<Item>
    content: List<NonHierarchicalData>
    laterChildren: List<Item>
}

这些 Item 可以按用户所需的任意方式嵌套。

  • 每个 Item 应根据其在层次结构中的位置提供最多五个操作,具体取决于其排列方式(可能不超过10种排列组合)。
  • 每个 NonHierarchicalData 也应根据其数据提供上下文菜单,最多可能有30个菜单项。由于数据包含用户输入,这些菜单项可能是唯一的。

一个现实状态可能包含大约30或40个这些 Item,每个 Item 具有5到10个 NonHierarchicalData 元素。基于这些数字,我可能会得到大约250个不同的上下文菜单,其中包含超过5000个菜单项。 在任何给定时间,用户可能只会打开一两个上下文菜单,然后选择一个操作,从而触发状态更改和重新渲染。

挑战

一方面,有很棒的现成库,比如react-contextmenu,它期望所有上下文菜单变体都要事先知道,并作为DOM的一部分创建,只有在需要时才显示出来。
另一方面,创建数百个这样的菜单,每个菜单中有数千个条目,仅仅为了在下一个状态更改后不可避免地重新创建前显示其中几十个,感觉有些违反直觉。

写这篇文章时,我想到的唯一方法就是在触发上下文菜单时发送一个操作,并将此单个上下文菜单包含在结果重新渲染的某种弹出对话框中。我想不出任何缺点。 - Jordan Running
但是似乎没有任何现有的上下文菜单库/组件涵盖这个标准用例。也就是说,至少需要部分自定义解决方案。 - Carsten
2个回答

5
自版本v2.3.1起,react-contextmenu提供了一个connectMenu()助手,以便将触发器的props传递到单个注册菜单中以更改其呈现方式。然而,在未来可能会对此进行重构,因此请在更新的版本中谨慎使用。
简而言之:您可以为ContextMenu设置一个包装器组件,并通过上述的connectMenu()助手使其能够接收触发器的props。
const MENU_ID = 'some-identifier';
const DynamicMenu = (props) => {
  const { id, trigger } = props;
  return (
    <ContextMenu id={id}>
      {trigger && <MenuItem>{trigger.itemLabel}</MenuItem>}
    </ContextMenu>
  );
};
const ConnectedMenu = connectMenu(MENU_ID)(DynamicMenu);

这个连接的包装器一个ContextMenu(这里是ConnectedMenu)只需要在某个父组件中包含一次,就可以用于具有不同内容的菜单的多个触发器。请注意,ContextMenuTriggerconnect属性必须转发其所有props或至少与正在构建的菜单相关的部分。例如:

const DynamicMenuExample = (props) => (
  <div>
    <ContextMenuTrigger id={MENU_ID} itemLabel='one'
        connect={props => props}>
      {'Click me for a menu with a "one" item.'}
    </ContextMenuTrigger>
    <ContextMenuTrigger id={MENU_ID}
        connect={() => ({itemLabel: 'other'})}>
      {'Click me for a menu with an "other" item.'}
    </ContextMenuTrigger>
    <ContextMenuTrigger id={MENU_ID} itemLabel='third'
        connect={props => ({ itemLabel: props.itemLabel})}>
      {'Click me for a menu with a "third" item.'}
    </ContextMenuTrigger>
    <ConnectedMenu />
  </div>
);

请参考该项目的DynamicMenu演示,获得完整的示例。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接