如何通过调用函数呈现弹出窗口?

How to render a popup by calling a function?

我想在 React 中渲染一个元素 通过调用一个函数

通常您会使用一个组件(假设是 Popup),该组件从状态中获取一个布尔值以使其显示或不显示,并使用一些回调处理程序更改它。像这样:

import React, { useState } from "react";

import Popup from "somecomponentlibrary";
import { Button } from "pathtoyourcomponents";

export const SomeComponent = () => {
    const [open, setOpen] = useState(false);

    return (
        <>
            <Button onClick={() => { setOpen(true); }}>
                this opens a modal
            </Button>
            <Popup type={"info"} open={open} timeout={1000}>
                text within modal
                <Button onClick={() => { setOpen(false); }}></Button>
            </Popup>
        </>
    );
};

我想知道是否可以像上面那样调用一些方法在屏幕上显示它,而不是像上面那样在组件中返回它:

import React from "react";

import { Button, popup } from "pathtoyourcomponents";

export const SomeComponent = () => {
    return (
        <>
            <Button onClick={() => { popup.info("text within modal", 1000); }}>
                this opens a modal
            </Button>
        </>
    );
};

如何编写 popup 函数以便以这种方式在 DOM 中呈现 Popup 组件?

弹出窗口可以呈现为一个新的单独的 React 应用程序,但仍然可以与主应用程序共享状态,如下所示。

import React, { useEffect } from "react";
import { render, unmountComponentAtNode } from "react-dom";

const overlay = {
  top: "0",
  height: "100%",
  width: "100%",
  position: "fixed",
  backgroundColor: "rgb(0,0,0)"
};

const overlayContent = {
  position: "relative",
  top: "25%",
  textAlign: "center",
  margin: "30px",
  padding: "20px",
  backgroundColor: "white"
};

let rootNode;
let containerNode;

function Modal({ children }) {
  useEffect(() => {
    return () => {
      if (rootNode) {
        rootNode.removeChild(containerNode);
      }

      containerNode = null;
    };
  }, []);

  function unmountModal() {
    if (containerNode) {
      unmountComponentAtNode(containerNode);
    }
  }

  return (
    <div style={overlay}>
      <div style={overlayContent}>
        {children}
        <button onClick={unmountModal}>Close Modal</button>
      </div>
    </div>
  );
}

/* additional params like props/context can be passed */
function renderModal(Component) {
  if (containerNode) {
    return;
  }

  containerNode = document.createElement("div");
  rootNode = document.getElementById("root");
  containerNode.setAttribute("id", "modal");
  rootNode.appendChild(containerNode);

  render(<Modal>{Component}</Modal>, containerNode);
}

const App = () => {
  const ModalBody = <p>This is a modal</p>;

  return (
    <div>
      <button onClick={() => renderModal(ModalBody)}>Open Modal</button>
    </div>
  );
};

render(<App />, document.getElementById("root"));

不久前我为一个弹出窗口做了命令式API,它允许你等待它直到它关闭,甚至接收调用者的输入(你也可以发回用户按下的按钮):

const PopupContext = createContext()

export const PopupProvider = ({children}) => {
    const [open, setOpen] = useState(false)
    const [input, setInput] = useState('')
    const resolver = useRef()

    const handleOpen = useCallback(() => {
        const { promise, resolve } = createDeferredPromise()
        resolver.current = resolve
        setInput('')
        setOpen(true)

        return promise
    }, [])

    const handleClose = useCallback(() => {
        resolver.current?.(input)
        setOpen(false)
    }, [])

    return <PopupContext.Provider value={handleOpen}>
        {children}
        <Popup type={"info"} open={open} timeout={1000} onClose={handleClose}>
            <input value={input} onChange={e => setValue(e.target.value)}/>
            <Button onClick={handleClose}/>
         </Popup>
    </PopupContext.Provider>
}

export const usePopup = () => {
    const context = useContext(PopupContext);
    if (!context)
        throw new Error('`usePopup()` must be called inside a `PopupProvider` child.')

    return context
}

// used to let await until the popup is closed
const createDeferredPromise = func => {
    let resolve, reject
    const promise = new Promise((res, rej) => {
        resolve = res
        reject = rej
        func?.(resolve, reject)
    })

    return { promise, resolve, reject }
}

您可以使用提供商包装您的应用:

return <PopupProvider>
    <App/>
</PopupProvider>

并在您的功能组件中使用它:

const MyComponent = props => {
    const popup = usePopup()
    
    return <Button onClick={e => {
            const input = await popup()
            console.log('popup closed with input: ' + input)
        }/>
}

你可以做更多有趣的事情,比如将提示文本传递给弹出函数以在弹出窗口中显示等。我会把它留给你。

您可能还想记住被包装的顶级组件,以避免在弹出窗口中重新呈现整个应用程序 open/close。

调用函数时可以使用ReactDOM.render渲染弹窗:

const node = document.createElement("div");
const popup = (message, {type, timeout}) => {
  document.body.appendChild(node);
  const PopupContent = () => {
    return (
      <Popup type={type} open={true} timeout={timeout}>
        {message}
        <button
          onClick={clear}
        >Close</button>
      </Popup >
    );
  };

  const clear = () => {
    ReactDOM.unmountComponentAtNode(node);
    node.remove();
  }
  
  ReactDOM.render(<PopupContent/>, node);
};

然后调用弹窗函数:

 <Button onClick={() => { popup("text within modal", {type: "info", timeout: 1000}); }}>
    this opens a modal
 </Button>

是的,我想你可以做到。


例如:

使用弹窗组件

import React, { useState } from "react";

import Popup from "somecomponentlibrary";
import { Button } from "pathtoyourcomponents";

export const SomeComponent = () => {
    const [open, setOpen] = useState(false);

    return (
        <>
            <Button onClick={() => { setOpen(true); }}>
                this opens a modal
            </Button>
            <Popup type={"info"} open={open} timeout={1000}>
                text within modal
                <Button onClick={() => { setOpen(false); }}></Button>
            </Popup>
        </>
    );
};

定义弹窗组件

const Popup = (props) => {
  return(
    <div style={{zIndex:props.open?"-100":"100", transition: `all ${props.timeout / 
         1000}s`, opacity: props.open?1:0}}>
        {props.children}
    </div>
  )
}

我觉得你可以自定义你喜欢的动画效果。