承诺对话

什么是对话框?

维基百科说:
图形用户界面中的对话框(英文对话框)是一个特殊的界面元素,是一个用于显示信息和(或)接收用户响应的窗口。 它之所以得名,是因为它执行计算机与用户的双向交互(“对话”):告诉用户某些信息,然后等待他的响应。

我们有兴趣
等待他的回答
换句话说,我们打开一个模式窗口以获取反馈并在此之后执行一些操作。 看起来不一样吗? 我也这么认为。

想象一个情况,我们有一个用于用户管理的应用程序。
情况如下。

在主页上,用户可以打开其他用户列表的模式窗口。
在列表中,您可以打开一个模态窗口,其中包含有关用户的信息,并且在此窗口中,还存在用于发送信件的表格。

发送信件时,用户会打开一个有关成功发送的模式窗口。
当用户关闭模式窗口时,他返回到用户列表的模式窗口,但是有一个按钮可以写另一个字母,当单击该按钮时,用户将转到用户页面。

要阅读,很难以序列图的形式来想象这个任务。

图片

现在,一切都变得更加简单。

从代码的角度来看,打开模式窗口是一个同步操作,我们可以将其打开,在其内部可以关闭,但是如果更改模式窗口中的数据,当关闭它时,您需要从那里获取数据呢?

一个简单的例子,从用户的模式窗口中,我们更改数据,返回到列表的模式窗口,您需要更新有关该用户的信息。

有点不同步...

当我们打开模块时,我们需要等待它关闭并获取用户输入的数据。 异步动作很好地实现了承诺。

实际上,在我们的图表中已经放置了承诺,我们只是将它们标记为行动。 您可以重做一点。

图片

现在,当用户打开模式窗口时,一切都变得简单了,我们等到他完成工作,然后在约定中调用解决方法。 听起来很简单,让我们开始吧。

我的主要框架是反应,因此我们将立即基于它进行操作。 为了能够从应用程序的任何部分打开模式窗口,我们将使用Context API。

首先,我们需要创建一个上下文以及将其存储的位置。

// ./Provider.js export const DialogContext = React.createContext(); export const Provider = ({ children, node, Layout, config }) => { const [instances, setInstances] = useState([]); const [events, setEvents] = useState([]); const context = { instances, setInstances, config, events, setEvents }; const Component = instances.map(instance => ( <Layout key={instance.instanceName} component={config[instance.instanceName]} {...instance} /> )); const context = { instances setInstances }; //   state     const child = useMemo(() => React.Children.only(children), [children]); return ( <DialogContext.Provider value={context}> <> {child} {createPortal(Component, node)} </> </DialogContext.Provider> ); }; 

这里的一切都很简单,我们使用第一个useState创建一个打开的模式窗口数组。 有点像堆栈。

第二个参数useState是必需的,以便添加引用以按承诺解决和拒绝。 我们将在下面看到。

我们通过门户重定向渲染,这样,如果z-index发生问题,我们就不必战斗。

布局是一个组件,它将作为所有模式窗口的基础组件。

config参数只是一个对象,其中键是模式窗口的标识符,而值是模式窗口的组成部分。

 //  config.js export const exampleInstanceName = 'modal/example'; export default { [exampleInstanceName]: React.lazy(() => import('./Example')), }; 

现在,我们将编写将打开模式窗口的方法的实现。

这将是一个钩子:

 export const useDialog = () => { const { setEvents, setInstances, config } = useContext(DialogContext); const open = instance => new Promise((resolve, reject) => { if (instance.instanceName in config) { setInstances(prevInstances => [...prevInstances, instance]); setEvents(prevEvents => [...prevEvents, { resolve, reject }]); } else { throw new Error(`${instance['instanceName']} don't exist in modal config`); } }); return { open }; }; 

该钩子返回一个开放函数,我们可以使用该函数来调用模式窗口。

 import { exampleInstanceName } from './config'; import { useDialog } from './useDialog'; const FillFormButton = () => { const { open } = useDialog(); const fillForm = () => open(exampleInstanceName) return <button onClick={fillForm}>fill form from modal</button> } 

在此选项中,我们将永远不会等待模式窗口关闭;我们需要添加方法来完成Promise:

 // ./Provider.js export const DialogContext = React.createContext(); export const Provider = ({ children, node, Layout, config }) => { const [instances, setInstances] = useState([]); const [events, setEvents] = useState([]); const close = useCallback(() => { const { resolve } = events[events.length - 1]; const resolveParams = { action: actions.close }; setInstances(prevInstances => prevInstances.filter((_, index) => index !== prevInstances.length - 1)); setEvents(prevEvents => prevEvents.filter((_, index) => index !== prevEvents.length - 1)); resolve(resolveParams); }, [events]); const cancel = useCallback((values): void => { const { resolve } = events[events.length - 1]; const resolveParams = { action: actions.cancel, values }; setInstances(prevInstances => prevInstances.filter((_el, index) => index !== prevInstances.length - 1)); setEvents(prevEvents => prevEvents.filter((_el, index) => index !== prevEvents.length - 1)); resolve(resolveParams); }, [events]); const success = useCallback((values) => { const { resolve } = events[events.length - 1]; const resolveParams = { action: actions.success, values }; setInstances(prevInstances => prevInstances.filter((_el, index) => index !== prevInstances.length - 1)); setEvents(prevEvents => prevEvents.filter((_el, index) => index !== prevEvents.length - 1)); resolve(resolveParams); }, [events]); const context = { instances, setInstances, config, events, setEvents }; const Component = instances.map(instance => ( <Layout key={instance.instanceName} component={config[instance.instanceName]} cancel={cancel} success={success} close={close} {...instance} /> )); const context = { instances setInstances }; //   state     const child = useMemo(() => React.Children.only(children), [children]); return ( <DialogContext.Provider value={context}> <> {child} {createPortal(Component, node)} </> </DialogContext.Provider> ); }; 

现在,当在Layout组件中或将这些方法传递给模式窗口组件时,将调用成功,取消或关闭方法,我们将获得必要的承诺。 在此处添加了诸如动作之类的概念,该行回答了对话完成的状态。 在执行模态窗口后执行一些操作时,这可能会派上用场:

 const { useState } from 'rect'; import { exampleInstanceName } from './config'; import { useDialog } from './useDialog'; const FillFormButton = () => { const [disabled, setDisabled] = useState(false); const { open } = useDialog(); const fillForm = () => open(exampleInstanceName) .then(({ action }) => { if (action === 'success') setDisabled(true); }); return <button onClick={fillForm} disabled={disabled}>fill form from modal</button> } 

仅此而已。 仍然需要将参数从模态窗口的传递添加到打开功能的模态窗口。 好吧,我认为您可以自己解决这个问题,但是如果您太懒了,那么可以在项目中使用一个现成的程序包

Source: https://habr.com/ru/post/zh-CN470824/


All Articles