我正在开发一个应用程序,在某些情况下需要显示确认对话框.

假设我想要删除一些东西,然后我会分派一个类似deleteSomething(id)的操作,这样一些Reducer就会捕捉到该事件,并填充对话框Reducer以显示它.

当这个对话提交时,我产生了怀疑.

  • 此组件如何根据调度的第一个操作调度正确的操作?
  • 动作创建者应该处理这个逻辑吗?
  • 我们可以在减速器内添加动作吗?

编辑:

更清楚地说:

deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)

所以我试图重用dialog组件.显示/隐藏对话框这不是问题,因为这可以在减速器中轻松完成.我想说明的是,如何根据在左侧启动流的操作从右侧分派操作.

推荐答案

Update:React 16.0至ReactDOM.createPortal link

Update:React的下一个版本(光纤:可能是16或17)将包括一种创建入口的方法:ReactDOM.unstable_createPortal() link


Use portals

丹·阿布拉莫夫的回答第一部分很好,但涉及很多样板.正如他所说,你也可以使用门户网站.我会详细介绍一下这个 idea .

门户的优势在于弹出窗口和按钮与react 树保持非常接近,使用props 进行非常简单的父/子通信:您可以轻松地处理与门户的异步操作,或者让父门户自定义门户.

什么是门户?

门户允许您在document.body内直接呈现深度嵌套在您的react 树中的元素.

例如,可以将以下react 树渲染到实体中:

<div className="layout">
  <div className="outside-portal">
    <Portal>
      <div className="inside-portal">
        PortalContent
      </div>
    </Portal>
  </div>
</div>

你得到的结果是:

<body>
  <div class="layout">
    <div class="outside-portal">
    </div>
  </div>
  <div class="inside-portal">
    PortalContent
  </div>
</body>

inside-portal node 已在<body>内部转换,而不是其正常的深嵌套位置.

When to use a portal

门户对于显示应该位于现有Reaction组件之上的元素特别有用:弹出窗口、下拉菜单、建议、热点

Why use a portal

No z-index problems anymore:门户允许渲染到<body>.如果你想显示一个弹出窗口或下拉列表,如果你不想对抗z索引问题,这是一个非常好的主意.门户元素以装入顺序添加到dodocument.body,这意味着除非您使用z-index,否则默认行为将是以装入顺序将门户堆叠在彼此之上.实际上,这意味着您可以从另一个弹出窗口中安全地打开一个弹出窗口,并确保第二个弹出窗口将显示在第一个弹出窗口的顶部,而无需考虑z-index.

In practice

Most simple: use local React state:如果您认为,对于一个简单的删除确认弹出窗口,使用Redux样板文件是不值得的,那么您可以使用一个门户,它大大简化了您的代码.对于这样的用例,交互是非常局部的,实际上是一个相当详细的实现细节,您真的关心热重新加载、时间旅行、操作日志(log)以及Redux带给您的所有好处吗?就我个人而言,在这种情况下,我不会使用本地州.代码变得非常简单:

class DeleteButton extends React.Component {
  static propTypes = {
    onDelete: PropTypes.func.isRequired,
  };

  state = { confirmationPopup: false };

  open = () => {
    this.setState({ confirmationPopup: true });
  };

  close = () => {
    this.setState({ confirmationPopup: false });
  };

  render() {
    return (
      <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
          <Portal>
            <DeleteConfirmationPopup
              onCancel={() => this.close()}
              onConfirm={() => {
                this.close();
                this.props.onDelete();
              }}
            />
          </Portal>
        )}
      </div>
    );
  }
}

Simple: you can still use Redux state:如果你真的想,你仍然可以用connect来 Select 是否显示DeleteConfirmationPopup.由于门户仍然深入嵌套在React树中,因此定制此门户的行为非常简单,因为您的父级可以向门户传递props .如果不使用门户,出于z-index个原因,通常需要在React树的顶部呈现弹出窗口,并且通常需要考虑"如何自定义根据用例生成的通用DeleteConfirmationPopup".通常,你会发现这个问题的解决方案相当粗糙,比如调度一个包含嵌套的确认/取消操作、翻译包密钥,或者更糟的是,一个渲染函数(或者其他不可分解的东西).你们不必对门户网站这么做,只需传递常规props 即可,因为DeleteConfirmationPopup只是DeleteButton岁的子元素

结论

门户对于简化代码非常有用.我不能再没有他们了.

请注意,门户实现还可以帮助您实现其他有用的功能,如:

  • 可达性
  • 使用快捷方式关闭门户
  • 处理外部点击(是否关闭门户)
  • 处理链接点击(是否关闭门户)
  • 门户树中提供的React上下文

react-portalreact-modal适用于弹出窗口、模态和覆盖,它们应该是全屏的,通常居中于屏幕中央.

react-tether对于大多数React开发者来说是未知的,但它是你能找到的最有用的工具之一.Tether允许您创建入口,但会相对于给定目标自动定位入口.这是完美的工具提示,下拉列表,热点,帮助框...如果你曾经遇到过位置absolute/relativez-index的任何问题,或者你的下拉列表超出了你的视口,Tether将为你解决所有这些问题.

例如,您可以轻松实现入职热点,单击后可扩展为工具提示:

Onboarding hotspot

这里是真正的生产代码.不能再简单了:)

<MenuHotspots.contacts>
  <ContactButton/>
</MenuHotspots.contacts>

Edit:刚刚发现react-gateway个允许将门户渲染到您 Select 的 node (不一定是主体)

Edit:似乎react-popper是一个不错的 Select ,以react 系绳.PopperJS是一个库,它只计算元素的适当位置,而不直接接触DOM,让用户 Select 何时何地放置DOM node ,而Tether直接附加到主体.

Edit:还有react-slot-fill个,这很有趣,可以通过允许将元素呈现到保留的元素槽中来帮助解决类似的问题,该保留的元素槽可以放在树中的任何位置

Javascript相关问答推荐

为什么新的Promises会在不需要的情况下被用来等待?

如果被1个Phaser JS抵消,我的倾斜碰撞

无法将nPM simplex-noise包导入在JS项目中工作

获取表格的左滚动位置

如何在不分配整个数组的情况下修改包含数组的行为主体?

为什么getRecord()会因为与_logger相关的错误而失败?(使用Hedera SDK)

ReactJS中的material UI自动完成类别

Phaser框架-将子对象附加到Actor

通过在页面上滚动来移动滚动条

调用removeEvents不起作用

WebGL 2.0无符号整数输入变量

在JS中拖放:检测文件

如何在使用rhandsontable生成表时扩展数字输入验证?

TypeError:无法分解';React2.useContext(...)';的属性';basename';,因为它为空

类构造函数忽略Reaction Native中的可选字段,但在浏览器中按预期工作

OpenAI转录API错误请求

如果没有页面重新加载Angular ,innerHTML属性绑定不会更新

警告框不显示包含HTML输入字段的总和

使用线性插值法旋转直线以查看鼠标会导致 skip

Jexl to LowerCase()和Replace()