【问题标题】:React portal popup window variableReact 门户弹出窗口变量
【发布时间】:2020-03-18 16:53:44
【问题描述】:

我目前正在尝试使用ReactDOM.createPortal 创建一个弹出窗口。 我已经通过https://github.com/facebook/react/issues/12355 并使用以下代码

class Window extends React.Component {
  constructor(props) {
    super(props);
    this.state = { win: null, el: null };
  }

  componentDidMount() {
    let win = window.open('', '', 'width=600,height=400');
    win.document.title = 'A React portal window';
    let el = document.createElement('div');
    win.document.body.appendChild(el);
    this.setState({ win, el });
  }

  componentWillUnmount() {
    this.state.win.close();
  }

  render() {
    const { el } = this.state;
    if (!el) {
      return null;
    }
    return ReactDOM.createPortal(this.props.children, el);
  }
}

主要问题是this.props.children 可以包含将事件处理程序注册为的组件

windows 变量仍然是指主窗口,而不是最近打开的弹出窗口。

https://codepen.io/ggcatu/pen/OJJGxYW?editors=0010 我想计算弹出 X & Y。 我不想将this.state.win 传递给新的孩子,因为它们可以包含库组件,它们只是直接调用window.screenX

任何已知的解决方法?

【问题讨论】:

  • 我遇到了同样的问题,你有解决方案吗? @加布里埃尔
  • 我之前遇到过这个问题,作为一种解决方法,我完全放弃了使用门户。根据您的应用程序的设计方式,这可能相对容易。基本上,我没有使用门户,而是在我的应用程序中设置了一个新的路由/页面,并请求在该页面上打开一个窗口。您可以通过窗口消息传递或查询参数共享状态。

标签: javascript reactjs popup


【解决方案1】:

我去年发了一篇关于这个东西的中等帖子,你不需要门户,门户与 dom 元素一起工作。 https://enetoolveda.medium.com/open-a-popup-window-react-db81c8c180e5

这里是代码https://codesandbox.io/s/keen-sea-2xow0?file=/src/home.js

假设我创建了一个 CRA 项目

我的 App.js 看起来像这样

import React from 'react';
import { BrowserRouter, Redirect, Route, Switch } from "react-router-dom";
import './App.css';
import { Home } from "./Home";
import { Son } from "./Son";

function App() {

  return (
    <div className="App">
      <BrowserRouter>
      <Switch>
        <Route exact path="/son" component={Son}/>
        <Route exact path="/" component={Home}/>
        <Redirect from="*" to="/" />
      </Switch>
      </BrowserRouter>
    </div>
  );
}

export default App;

我们需要组件化实际的弹出窗口和容器组件。

开始的秘密是弹出窗口没有 知道它是一个外部窗口。它需要对此不可知。

创建开窗器

import React, { useEffect } from "react";

let browser = window.self;
let popup;
let timer;

// This function is what the name says.
// it checks whether the popup still open or not
function watcher() {
  // if popup is null then let's clean the intervals.
  if (popup === null) {
    clearInterval(timer);
    timer = undefined;
    // if popup is not null and it is not closed, then let's set the focus on it... maybe...
  } else if (popup !== null && !popup.closed) {
    popup.focus();
    // if popup is closed, then let's clean errthing.
  } else if (popup !== null && popup.closed) {
    clearInterval(timer);
    browser.focus();
    // the onCloseEventHandler it notifies that the child has been closed.
    browser.fromSon({ type: "info", payload: { message: "child was closed" } });
    timer = undefined;
    popup = undefined;
  }
}

export const WindowOpener = (props) => {
  useEffect(() => {
    browser.fromSon = (res) => {
      props.bridge(res);
    };
  }, [props]);

  /**
   * opens a child
   */
  const onClickHandler = (evt) => {
    const { url, name, opts } = props;
    // if there is  already a child open, let's set focus on it
    if (popup && !popup.closed) {
      popup.focus();

      return;
    }
    // we open a new window.
    popup = browser.open(url, name, opts);

    setTimeout(() => {
      // The opener object is created once and only if a window has a parent
      browser.fromSon({ type: "info", payload: { message: "child was open" } });
    }, 0);

    if (timer === null) {
      // each two seconds we check if the popup still open or not
      timer = setInterval(watcher, 2000);
    }

    return;
  };

  const { children } = props;

  return (
    <button type="button" onClick={onClickHandler}>
      {children}
    </button>
  );
};

现在让我们创建儿子

import React, { useEffect, useState } from "react";

export const Son = () => {
  const [message, setMessage] = useState("");

  /** This Component only renders on a popup window */
  useEffect(() => {
    if (!window.opener) {
      window.location.pathname = "/";
    }
  }, []);

  const onChangeHandler = (evt) => {
    const { value } = evt.target;

    setMessage(value);
    window.opener.fromSon(value);
  };

  return (
    <div>
      <input type="text" value={message} onChange={onChangeHandler} />
    </div>
  );
};

主页组件

import React from "react";
import logo from "./logo.svg";
import { WindowOpener } from "./window-opener";
export class Home extends React.Component {
    constructor (props) {
        super(props);

        this.state = {
            message: ""
        }
        this.sonResponse = this.sonResponse.bind(this);
    }

    sonResponse (err, res) {
        if (err) {
            this.setState({ message: res })
        }
        this.setState({ message: res })
    }

    render () {
        const {message} = this.state;
        return (
            <div>
                <header className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <p>
                        Edit <code>src/App.js</code> and save to reload.
        </p>
                    <a
                        className="App-link"
                        href="https://reactjs.org"
                        target="_blank"
                        rel="noopener noreferrer"
                    >
                        {message}
        </a>
                    <WindowOpener
                        url="http://localhost:3000/son"
                        bridge={this.sonResponse}
                    >
                        Open Browser
      </WindowOpener>
                </header>

            </div>
        )
    }
}

【讨论】:

    【解决方案2】:

    您可以尝试包装孩子:

    return ReactDOM.createPortal(
    <div id='portal'>{this.props.children}</div>, el);
    

    根据需要设置样式,然后从子组件中添加事件监听器?

    const portal = document.getElementById('portal');
    portal.addEventListener(etc)
    

    【讨论】:

      猜你喜欢
      • 2018-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 1970-01-01
      • 2023-02-20
      • 1970-01-01
      • 2017-09-24
      相关资源
      最近更新 更多