如何在 Redux 中显示一个执行异步操作的模态对话框?

本文介绍了如何在 Redux 中显示一个执行异步操作的模态对话框?的处理方法,对大家解决问题具有一定的参考价值

问题描述

我正在构建一个需要在某些情况下显示确认对话框的应用.

假设我想删除一些东西,然后我将发送一个类似 deleteSomething(id) 的操作,这样一些 reducer 将捕获该事件并填充对话框 reducer 以显示它.

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

  • 该组件如何根据分派的第一个动作来分派正确的动作?
  • 动作创建者应该处理这个逻辑吗?
  • 我们可以在 reducer 中添加动作吗?

为了更清楚:

deleteThingA(id) =>显示带有问题的对话框 =>deleteThingAremotely(id)createThingB(id) =>显示带有问题的对话框 =>createThingBremotely(id)

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

解决方案

更新:React 16.0 通过 ReactDOM.createPortal

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

<MenuHotspots.contacts><联系按钮/></MenuHotspots.contacts>

<小时>

编辑:刚刚发现 react-gateway 允许渲染进入您选择的节点的门户(不一定是主体)

编辑:看来 react-popper 可能是不错的替代反应系绳.PopperJS 是一个库,它只计算元素的适当位置,而不直接接触 DOM,让用户选择他想要放置 DOM 节点的位置和时间,而 Tether 直接附加到正文.

编辑:还有 react-slot-fill这很有趣,并且可以通过允许将元素渲染到保留元素插槽来帮助解决类似问题,您可以将其放置在树中的任何位置

I'm building an app that needs to show a confirm dialog in some situations.

Let's say I want to remove something, then I'll dispatch an action like deleteSomething(id) so some reducer will catch that event and will fill the dialog reducer in order to show it.

My doubt comes when this dialog submits.

  • How can this component dispatch the proper action according to the first action dispatched?
  • Should the action creator handle this logic?
  • Can we add actions inside the reducer?

edit:

to make it clearer:

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

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

So I'm trying to reuse the dialog component. Showing/hiding the dialog it's not the problem as this can be easily done in the reducer. What I'm trying to specify is how to dispatch the action from the right side according to the action that starts the flow in the left side.

解决方案

Update: React 16.0 introduced portals through ReactDOM.createPortal link

Update: next versions of React (Fiber: probably 16 or 17) will include a method to create portals: ReactDOM.unstable_createPortal() link


Use portals

Dan Abramov answer first part is fine, but involves a lot of boilerplate. As he said, you can also use portals. I'll expand a bit on that idea.

The advantage of a portal is that the popup and the button remain very close into the React tree, with very simple parent/child communication using props: you can easily handle async actions with portals, or let the parent customize the portal.

What is a portal?

A portal permits you to render directly inside document.body an element that is deeply nested in your React tree.

The idea is that for example you render into body the following React tree:

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

And you get as output:

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

The inside-portal node has been translated inside <body>, instead of its normal, deeply-nested place.

When to use a portal

A portal is particularly helpful for displaying elements that should go on top of your existing React components: popups, dropdowns, suggestions, hotspots

Why use a portal

No z-index problems anymore: a portal permits you to render to <body>. If you want to display a popup or dropdown, this is a really nice idea if you don't want to have to fight against z-index problems. The portal elements get added do document.body in mount order, which means that unless you play with z-index, the default behavior will be to stack portals on top of each others, in mounting order. In practice, it means that you can safely open a popup from inside another popup, and be sure that the 2nd popup will be displayed on top of the first, without having to even think about z-index.

In practice

Most simple: use local React state: if you think, for a simple delete confirmation popup, it's not worth to have the Redux boilerplate, then you can use a portal and it greatly simplifies your code. For such a use case, where the interaction is very local and is actually quite an implementation detail, do you really care about hot-reloading, time-traveling, action logging and all the benefits Redux brings you? Personally, I don't and use local state in this case. The code becomes as simple as:

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: if you really want to, you can still use connect to choose whether or not the DeleteConfirmationPopup is shown or not. As the portal remains deeply nested in your React tree, it is very simple to customize the behavior of this portal because your parent can pass props to the portal. If you don't use portals, you usually have to render your popups at the top of your React tree for z-index reasons, and usually have to think about things like "how do I customize the generic DeleteConfirmationPopup I built according to the use case". And usually you'll find quite hacky solutions to this problem, like dispatching an action that contains nested confirm/cancel actions, a translation bundle key, or even worse, a render function (or something else unserializable). You don't have to do that with portals, and can just pass regular props, since DeleteConfirmationPopup is just a child of the DeleteButton

Conclusion

Portals are very useful to simplify your code. I couldn't do without them anymore.

Note that portal implementations can also help you with other useful features like:

  • Accessibility
  • Espace shortcuts to close the portal
  • Handle outside click (close portal or not)
  • Handle link click (close portal or not)
  • React Context made available in portal tree

react-portal or react-modal are nice for popups, modals, and overlays that should be full-screen, generally centered in the middle of the screen.

react-tether is unknown to most React developers, yet it's one of the most useful tools you can find out there. Tether permits you to create portals, but will position automatically the portal, relative to a given target. This is perfect for tooltips, dropdowns, hotspots, helpboxes... If you have ever had any problem with position absolute/relative and z-index, or your dropdown going outside of your viewport, Tether will solve all that for you.

You can, for example, easily implement onboarding hotspots, that expands to a tooltip once clicked:

Real production code here. Can't be any simpler :)

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


Edit: just discovered react-gateway which permits to render portals into the node of your choice (not necessarily body)

Edit: it seems react-popper can be a decent alternative to react-tether. PopperJS is a library that only computes an appropriate position for an element, without touching the DOM directly, letting the user choose where and when he wants to put the DOM node, while Tether appends directly to the body.

Edit: there's also react-slot-fill which is interesting and can help solve similar problems by allowing to render an element to a reserved element slot that you put anywhere you want in your tree

这篇关于如何在 Redux 中显示一个执行异步操作的模态对话框?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,WP2

admin_action_{$_REQUEST[‘action’]}

do_action( "admin_action_{$_REQUEST[‘action’]}" )动作钩子::在发送“Action”请求变量时激发。Action Hook: Fires when an ‘action’ request variable is sent.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$_REQUEST['action']引用从GET或POST请求派生的操作。源码(Source)更新版本源码位置使用被使用2.6.0 wp-admin/admin.php:...

日期:2020-09-02 17:44:16 浏览:1127

admin_footer-{$GLOBALS[‘hook_suffix’]}

do_action( "admin_footer-{$GLOBALS[‘hook_suffix’]}", string $hook_suffix )操作挂钩:在默认页脚脚本之后打印脚本或数据。Action Hook: Print scripts or data after the default footer scripts.目录锚点:#说明#参数#源码说明(Description)钩子名的动态部分,$GLOBALS['hook_suffix']引用当前页的全局钩子后缀。参数(Parameters)参数类...

日期:2020-09-02 17:44:20 浏览:1032

customize_save_{$this->id_data[‘base’]}

do_action( "customize_save_{$this-&gt;id_data[‘base’]}", WP_Customize_Setting $this )动作钩子::在调用WP_Customize_Setting::save()方法时激发。Action Hook: Fires when the WP_Customize_Setting::save() method is called.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_data...

日期:2020-08-15 15:47:24 浏览:775

customize_value_{$this->id_data[‘base’]}

apply_filters( "customize_value_{$this-&gt;id_data[‘base’]}", mixed $default )过滤器::过滤未作为主题模式或选项处理的自定义设置值。Filter Hook: Filter a Customize setting value not handled as a theme_mod or option.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分,$this->id_date['base'],指的是设置...

日期:2020-08-15 15:47:24 浏览:866

get_comment_author_url

过滤钩子:过滤评论作者的URL。Filter Hook: Filters the comment author’s URL.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/comment-template.php:32610...

日期:2020-08-10 23:06:14 浏览:903

network_admin_edit_{$_GET[‘action’]}

do_action( "network_admin_edit_{$_GET[‘action’]}" )操作挂钩:启动请求的处理程序操作。Action Hook: Fires the requested handler action.目录锚点:#说明#源码说明(Description)钩子名称的动态部分$u GET['action']引用请求的操作的名称。源码(Source)更新版本源码位置使用被使用3.1.0 wp-admin/network/edit.php:3600...

日期:2020-08-02 09:56:09 浏览:848

network_sites_updated_message_{$_GET[‘updated’]}

apply_filters( "network_sites_updated_message_{$_GET[‘updated’]}", string $msg )筛选器挂钩:在网络管理中筛选特定的非默认站点更新消息。Filter Hook: Filters a specific, non-default site-updated message in the Network admin.目录锚点:#说明#参数#源码说明(Description)钩子名称的动态部分$_GET['updated']引用了非默认的...

日期:2020-08-02 09:56:03 浏览:834

pre_wp_is_site_initialized

过滤器::过滤在访问数据库之前是否初始化站点的检查。Filter Hook: Filters the check for whether a site is initialized before the database is accessed.目录锚点:#源码源码(Source)更新版本源码位置使用被使用 wp-includes/ms-site.php:93910...

日期:2020-07-29 10:15:38 浏览:809

WordPress 的SEO 教学:如何在网站中加入关键字(Meta Keywords)与Meta 描述(Meta Description)?

你想在WordPress 中添加关键字和meta 描述吗?关键字和meta 描述使你能够提高网站的SEO。在本文中,我们将向你展示如何在WordPress 中正确添加关键字和meta 描述。为什么要在WordPress 中添加关键字和Meta 描述?关键字和说明让搜寻引擎更了解您的帖子和页面的内容。关键词是人们寻找您发布的内容时,可能会搜索的重要词语或片语。而Meta Description则是对你的页面和文章的简要描述。如果你想要了解更多关于中继标签的资讯,可以参考Google的说明。Meta 关键字和描...

日期:2020-10-03 21:18:25 浏览:1619

谷歌的SEO是什么

SEO (Search Engine Optimization)中文是搜寻引擎最佳化,意思近于「关键字自然排序」、「网站排名优化」。简言之,SEO是以搜索引擎(如Google、Bing)为曝光媒体的行销手法。例如搜寻「wordpress教学」,会看到本站的「WordPress教学:12个课程…」排行Google第一:关键字:wordpress教学、wordpress课程…若搜寻「网站架设」,则会看到另一个网页排名第1:关键字:网站架设、架站…以上两个网页,每月从搜寻引擎导入自然流量,达2万4千:每月「有机搜...

日期:2020-10-30 17:23:57 浏览:1262