我去年发了一篇关于这个东西的中等帖子,你不需要门户,门户与 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>
)
}
}