javascript - 在重新渲染期间保持 React Portal 显示在外部窗口上
问题描述
我对应用程序这一部分的目标是,当用户检索信息时,它会显示在外部窗口中。我通过使用单独的组件来完成这项工作,但如果我打开第二个窗口,第一个门户中的内容就会消失。我认为这与状态有关。因此,我用useReducer重写了父容器,不再引用子组件而是尝试在父组件中使用createPortal。虽然窗口弹出打开正常,状态更新正常,但门户永远不会将内容呈现为第一个孩子。代码如下。
import React, {useEffect, useState, useReducer} from "react";
import fiostv from "../api/fiostv";
import Accordion from "./Accordion";
import "./App.css";
import ReactDOM from "react-dom";
const initialState = []
function reducer(state, action) {
/*
TODO: needs a 'REMOVE' action,
TODO: not using the open property. Use it or delete it
*/
switch (action.type) {
case "ADD":
return [
...state,
{
win: null,
title: action.payload.device,
content: action.payload.data,
open: false
}
];
default:
return state;
}
}
function ConfigFetcher() {
const [state, dispatch] = useReducer(reducer, initialState);
const [device, setDevice] = useState("");
const [locations, setLocations] = useState(null );
const [selectedOption, setSelectedOption] = useState(null);
const [content, setContent] = useState(null);
const [firstDate, setFirstDate] = useState(null);
const [secondDate, setSecondDate] = useState(null);
async function fetchData(url){
let response = await fiostv.get(url);
response = await response.data;
return response;
}
function formSubmit(e) {
e.preventDefault();
if (!firstDate || !secondDate) {
fetchData(`/backups/${selectedOption}`).then(data => {
if (!state.find(o => o.title === device)) {
dispatch({type: "ADD", payload: {device, data}})
}
});
}
else {
fetchData(`/diffs/${selectedOption}/${firstDate}/${secondDate}`).then(data => {
if (data.includes("Error:")) {
alert(data)
}
else {
setContent(data)
}
});
}
}
const createPortal = () => {
if (state.length > 0 ) {
const obj = state.find(o => o.title === device)
const pre = document.createElement('pre');
const div = document.createElement('div');
div.appendChild(pre)
const container = document.body.appendChild(div);
obj.win = window.open('',
obj.title,
'width=600,height=400,left=200,top=200'
);
obj.win.document.title = obj.title;
obj.win.document.body.appendChild(container);
if (obj) {
return (
ReactDOM.createPortal(
obj.content, container.firstChild
)
);
} else {
return null
}
}
}
useEffect(() => {
createPortal();
}, [state])
const rearrange = (date_string) => {
let d = date_string.split('-');
let e = d.splice(0, 1)[0]
d.push(e)
return d.join('-');
}
function onDateChange(e) {
if (content != null) {
setContent(null);
}
e.preventDefault();
if (e.target.name === "firstDate"){
setFirstDate(rearrange(e.target.value));
}
else {
setSecondDate(rearrange(e.target.value));
}
}
const onValueChange = (e) => {
if (content != null) {
setContent(null);
}
setSelectedOption(e.target.value);
setDevice(e.target.name)
}
const Values = (objects) => {
return (
<form onSubmit={formSubmit} className={"form-group"}>
<fieldset>
<fieldset>
<div className={"datePickers"}>
<label htmlFor={"firstDate"}>First config date</label>
<input name={"firstDate"} type={"date"} onChange={onDateChange}/>
<label htmlFor={"secondDate"}>Second config date</label>
<input name={"secondDate"} type={"date"} onChange={onDateChange}/>
</div>
</fieldset>
{Object.values(objects).map(val => (
<div className={"radio"}
style={{textAlign: "left", width: "100%"}}>
<input type={"radio"}
name={val.sysname}
value={val.uuid}
checked={selectedOption === val.uuid}
onChange={onValueChange}
style={{verticalAlign: "middle"}}
/>
<label
style={{verticalAlign: "middle", textAlign: "left", width: "50%"}}>
{val.sysname}
</label>
</div>
))}
<div className={"formButtons"}>
<button type={"submit"} className={"btn-submit"}>Submit</button>
</div>
</fieldset>
</form>
);
}
useEffect(() => {
fetchData('/devicelocations').then(data => {setLocations(data)});
}, []);
解决方案
由于 SPA 的性质,这不会像我想象的那样工作。当打开第二个窗口时,组件产生的内容总是会在新内容显示在新窗口中时“卸载”。最好的办法是只打开一个窗口并确保你有清理代码。例如,当用户关闭窗口时,有一个侦听器通过调度清理状态。如果用户没有关闭窗口但渲染了另一个组件,请确保 useEffect 挂钩中存在清理函数来清理任何状态并关闭窗口。
推荐阅读
- arrays - MIPS中的程序,它打印数组中最小值索引和最大值索引之间的所有元素
- ubuntu-20.04 - 如何将 Windows Ubuntu20.04 LTS 连接到我的 Xming 服务器
- php - 目前无法处理此请求。HTTP ERROR 500 在源代码中找不到错误
- .net - 从 Travis 构建服务器运行 docker-compose up
- python - 是否可以从 C# Form App 运行 Anaconda 脚本?
- async-await - 我在第 10 行收到了一个断言错误,即“预期未定义等于 '2 sec'”......为什么会这样?
- ios - 从随机字典键值创建数组
- javascript - Json / Discord.js:将基于输入的内容添加到 json 文件
- c# - NuGet 包 Microsoft.Toolkit.Uwp.Notifications 版本 7.0.2 无法识别 UWP TileContent
- mysql - mysql数据库表的foregin键中的自动inrement不起作用