Dialogues sur promesse

Qu'est-ce qu'une boĂźte de dialogue?

Wikipedia dit ce qui suit:
Une boĂźte de dialogue (boĂźte de dialogue en anglais) dans une interface utilisateur graphique est un Ă©lĂ©ment d'interface spĂ©cial, une fenĂȘtre conçue pour afficher des informations et (ou) recevoir une rĂ©ponse de l'utilisateur. Il a obtenu son nom parce qu'il effectue une interaction bidirectionnelle ordinateur-utilisateur ("dialogue"): informer l'utilisateur quelque chose et attendre une rĂ©ponse de sa part.

Nous sommes intéressés par
attendant une réponse de sa part
En d'autres termes, nous ouvrons une fenĂȘtre modale pour obtenir des commentaires et faire quelque chose aprĂšs cela. Ça ne ressemble Ă  rien? Et je le pensais.

Imaginez une situation, nous avons une application pour la gestion des utilisateurs.
Le scénario est le suivant.

Sur la page principale, l'utilisateur peut ouvrir une fenĂȘtre modale pour une liste d'autres utilisateurs.
Dans la liste, vous pouvez ouvrir une fenĂȘtre modale avec des informations sur l'utilisateur, Ă©galement dans cette fenĂȘtre il y a un formulaire pour envoyer des lettres.

Lors de l'envoi d'une lettre, l'utilisateur ouvre une fenĂȘtre modale sur l'envoi rĂ©ussi.
Lorsque l'utilisateur ferme la fenĂȘtre modale, il revient Ă  la fenĂȘtre modale de la liste des utilisateurs, mais il y a un bouton pour Ă©crire une autre lettre, lorsque l'utilisateur clique dessus, il accĂšde Ă  la page de l'utilisateur.

À lire, il est assez difficile d'imaginer cette tĂąche sous la forme d'un diagramme de sĂ©quence.

image

Maintenant, tout est beaucoup plus simple.

Du point de vue du code, l'ouverture d'une fenĂȘtre modale est une action synchrone, nous pouvons l'ouvrir, Ă  l'intĂ©rieur elle peut se fermer, mais qu'en est-il si la modification des donnĂ©es dans la fenĂȘtre modale, lors de sa fermeture, vous devez obtenir des donnĂ©es Ă  partir de lĂ ?

Un exemple simple, Ă  partir de la fenĂȘtre modale de l'utilisateur, nous modifions les donnĂ©es, revenant Ă  la fenĂȘtre modale de la liste, vous devez mettre Ă  jour les informations sur cet utilisateur.

Ça sent l'asynchronisme ...

Lorsque nous ouvrons le module, nous devons attendre sa fermeture et obtenir les donnĂ©es entrĂ©es par l'utilisateur. Les actions asynchrones sont trĂšs bien mises en Ɠuvre avec des promesses.

En fait, des promesses sont déjà posées dans notre diagramme, nous les marquons simplement comme des actions. Vous pouvez le refaire un peu.

image

Maintenant, tout devient simple lorsque l'utilisateur ouvre la fenĂȘtre modale, nous attendons qu'il termine son travail, aprĂšs quoi la rĂ©solution est appelĂ©e au promis. Cela semble simple, commençons.

Mon cadre principal est une rĂ©action, nous allons donc le faire immĂ©diatement en fonction de cela. Afin de pouvoir ouvrir des fenĂȘtres modales Ă  partir de n'importe quelle partie de l'application, nous utiliserons l'API Context.

Tout d'abord, nous devons crĂ©er un contexte et un endroit oĂč il sera stockĂ©.

// ./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> ); }; 

Tout est simple ici, nous utilisons le premier useState pour crĂ©er un tableau de fenĂȘtres modales ouvertes. Quelque chose comme une pile.

Le second, useState, est nécessaire pour ajouter des références à résoudre et à rejeter à la promesse. Nous verrons cela ci-dessous.

Nous redirigeons le rendu via le portail afin de ne pas avoir Ă  nous battre si quelque chose se produit avec le z-index.

La disposition est un composant qui sera le composant de base de toutes les fenĂȘtres modales.

Le paramĂštre config est juste un objet, oĂč la clĂ© est l'identifiant de la fenĂȘtre modale et la valeur est le composant de la fenĂȘtre modale.

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

Nous Ă©crivons maintenant une implĂ©mentation de la mĂ©thode qui ouvrira des fenĂȘtres modales.

Ce sera le crochet:

 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 }; }; 

Le hook renvoie une fonction ouverte que nous pouvons utiliser pour appeler une fenĂȘtre modale.

 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> } 

Dans cette option, nous n'attendrons jamais la fermeture de la fenĂȘtre modale; nous devons ajouter des mĂ©thodes pour tenir la promesse:

 // ./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> ); }; 

Maintenant, lorsque dans le composant Layout ou s'il transmet ces mĂ©thodes au composant de fenĂȘtre modale, les mĂ©thodes success, cancel ou close seront appelĂ©es, nous aurons la rĂ©solution Ă  la promesse nĂ©cessaire. Ici, un concept tel que l'action est ajoutĂ©, c'est une ligne qui rĂ©pond dans quel Ă©tat le dialogue a Ă©tĂ© achevĂ©. Cela peut ĂȘtre utile lorsque nous effectuons une action aprĂšs l'exĂ©cution de la fenĂȘtre modale:

 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> } 

C’est tout. Il reste Ă  ajouter le transfert des paramĂštres de la fenĂȘtre modale Ă  la fenĂȘtre modale de la fonction ouverte. Eh bien, je pense que vous pouvez gĂ©rer cela vous-mĂȘme, mais si vous ĂȘtes trop paresseux, il existe un package prĂȘt Ă  l'emploi que vous pouvez utiliser dans vos projets.

Source: https://habr.com/ru/post/fr470824/


All Articles