reactjs - 如何在不更新状态的情况下显示模态
问题描述
我整整一个工作日都在旋转我的齿轮,试图弄清楚如何做到这一点。我有一个呈现 PowerBi 嵌入式报表的组件。我在页面上有一个按钮,需要弹出一个 Bootstrap Modal 包装的组件。我可以通过状态正常执行此操作,但由于嵌入了 PowerBi 报告,我无法更改状态(因为这将重新呈现 PowerBI 报告,这需要时间,并且还会重置报告)。我曾尝试使用 useRef() 来存储此变量,但显然它不会通知 Modal 组件的更改。
想法?有什么建议吗?
import React, {useEffect, useRef, useState,useMemo} from 'react';
import ReportManager from '../managers/reportManager';
import SubscribeModel from '../components/subscription/SubscribeModal';
// @ts-ignore
import Report from 'powerbi-report-component';
import {FaChartLine, FaChartBar, FaDesktop, FaPrint, FaMailBulk} from 'react-icons/fa';
import PreloadManager from '../managers/preloadManager';
// @ts-ignore
import LoadingOverlay from 'react-loading-overlay';
import Preloader from '../components/Preloader';
interface ReportConfig {
id : string;
name : string;
webUrl : string;
embedUrl : string; //Embed URL
datasetId : string; //Report ID
embedToken : string; //Embed Token
}
class ReportConfigReal implements ReportConfig {
'id' : string;
'name' : string;
'webUrl' : string;
'embedUrl' : string;
'datasetId' : string;
'embedToken' : string;
'expiration' : string;
}
const Reports = (props : any) => {
const [config,
setConfig] = useState({
id: "",
name: "",
webUrl: "",
embedUrl: "",
embedToken: "",
expiration: ""
});
const [currentUser,
setCurrentUser] = useState(props.currentuser);
const [reportList,
setReportList] = useState(new Array());
const [reportId,
setReportId] = useState(props.match.params.report_id);
const [loading,
setLoading] = useState(false);
const [reportsLoading,
setReportsLoading] = useState(false);
const [report,
setReport] = useState(null);
const [subModal,
setSubModal] = useState(false);
let reportManager : ReportManager = new ReportManager();
let preloadManager : PreloadManager = new PreloadManager();
const currentPage = useRef(null);
const showSubscribe = useRef(null);
const createExpiration = (minutes : number) => {
const date = new Date();
return new Date(date.getTime() + minutes * 60000);
};
const onLoadAndSetTokenListener = (reportFromCallback : any) => {
setTokenExpirationListener(config.expiration, 1);
setReport(reportFromCallback);
};
const setTokenExpirationListener = (tokenExpiration : string, minutesToRefresh = 2) => {
// time until token refresh in milliseconds
const currentTime = Date.now();
const expiration = Date.parse(tokenExpiration);
const safetyInterval = minutesToRefresh * 60 * 1000;
let timeout = expiration - currentTime - safetyInterval;
// if token already expired, generate new token and set the access token
if (timeout <= 0) {
console.log('Updating Report Embed Token');
updateToken() // set timeout so minutesToRefresh minutes before token expires, token will be
// updated;
} else {
console.log('Report Embed Token will be updated in ' + timeout + ' milliseconds.');
setTimeout(() => {
console.log('Set time out');
updateToken();
}, timeout);
}
};
const updateToken = () => {
console.log('Update token called');
// Generate new EmbedToken (Axios call to get config/ token)
reportManager
.generateEmbedToken(currentUser)
.then((res : any) => {
// Set AccessToken (Use the report object)
if (report !== null) {
report
.setAccessToken(res.embedToken)
.then(() => {
console.log('new token set');
setTokenExpirationListener(createExpiration(2).toString(), 1);
})
.catch((err : any) => console.error('Error setting token', err));
}
});
};
const handleFullScreen = () => {
var elem = document.documentElement;
elem.requestFullscreen();
};
const handlePrint = () => {
window.print();
};
const handleSubscribe = () => {
console.log("page", currentPage.current);
console.log("showsub", showSubscribe);
};
const handleReportChange = (e : any) => {
window.location.href = '/reports/' + e;
};
const handleDataSelected = (data : any) => {
console.log("data selected", data);
};
const handlePageChange = (data : any) => {
console.log("page changed", data);
currentPage.current = data.newPage;
};
useEffect(() => {
let promiseList : any = [];
setLoading(true);
setReportsLoading(true);
if (currentUser && currentUser.allowedReports.length !== 0) {
currentUser
.allowedReports
.forEach((report : string) => {
promiseList.push(reportManager.getReportInfo(currentUser, report).then((info : any) => {
return info;
}),);
});
}
Promise
.all(promiseList)
.then(values => {
setReportList(values);
});
if (currentUser && currentUser.allowedReports.length !== 0 && currentUser.authenticated) {
if (reportId === undefined) {
reportManager
.getReport(currentUser)
.then(data => {
setConfig(data);
setLoading(false);
setReportsLoading(false);
})
.catch(error => {
console.log(error);
});
} else {
reportManager
.getReportInfo(currentUser, reportId)
.then((info : any) => {
setConfig(info);
setLoading(false);
setReportsLoading(false);
});
}
}
}, []);
return (
<LoadingOverlay active={loading} spinner={< Preloader />}>
<div>
<div
className={reportsLoading !== true
? 'slide-wrapper'
: 'col-sm-12 loading-delete'}>
<div id="slide-report">
<div className="menu_icon">
<div>
<FaChartLine color="#003267" size="30"/>
</div>
<span>Reports</span>
</div>
<div className="menu_content">
<ul>
{reportList.map((report, index) => {
return (
<li key={index} onClick={() => handleReportChange(report.id)}>
<FaChartBar className="icon-slide" color="#fff" size="30"/>
<span>{report
.name
.slice(0, 25)}</span>
</li>
);
})}
</ul>
</div>
</div>
<div id="slide-full-screen">
<div className="menu_icon" onClick={handleFullScreen}>
<div>
<FaDesktop color="#003267" size="30"/>
</div>
<span>Full Screen</span>
</div>
</div>
<div id="slide-print">
<div className="menu_icon" onClick={handlePrint}>
<div>
<FaPrint color="#003267" size="30"/>
</div>
<span>Print</span>
</div>
</div>
<div id="slide-subscribe">
<div className="menu_icon" onClick={handleSubscribe}>
<div>
<FaMailBulk color="#003267" size="30"/>
</div>
<span>Subscribe</span>
</div>
</div>
</div>
<div
key={reportId}
className={loading !== true
? 'col-sm-12'
: 'col-sm-12 loading-delete'}>
<Report
className={reportId}
embedType="report"
tokenType="Embed"
pageName={reportId}
accessToken={config.embedToken}
embedUrl={config.embedUrl}
embedId={config.id}
permissions="All"
reportMode="view"
onLoad={onLoadAndSetTokenListener}
onPageChange={handlePageChange}
onSelectData={handleDataSelected}
style={{
height: '90vh',
width: '100%',
display: 'block',
top: '0',
left: '0',
position: 'absolute',
border: '0',
padding: '20px',
background: '#fff',
zIndex: '1'
}}/>
</div>
</div>
<SubscribeModel show={false} />
</LoadingOverlay>
);
};
export default Reports;
解决方案
这看起来是错误的做法,但这将打开一个模式而无需更新状态。您是否考虑过查看componentShouldUpdate?powerBiReport 也不应该重新渲染,除非传递给它的任何道具已经改变或报告本身的状态已经改变,所以考虑看看那里。
/* -- snip -- */
onButtonClick={() => createModal(<YourModal onClose={removeModal}/>)}
/* -- snip -- */
const createModal(component) {
const container = document.appendChild("div");
container.id = "modal-container";
ReactDOM.render(container, component);
}
const removeModal() {
const container = document.getElementById("modal-container");
ReactDOM.unmountComponentAtNode(container);
}
推荐阅读
- r - 如何使用 R 删除不需要的引号?
- python - 无法通过 Python 退出 Bitmex 测试网的 websocket 连接
- javascript - 获取 Vuetify 数据表中过滤项目的长度
- matrix - Power Bi 矩阵中的自定义总列
- symfony - 序列化器 - 规范化器的序列化器对象需要什么才能将 json 反序列化为对象
- html - 将文本和问题与 HTML 中的文本区域框分开的方法
- python - 如何在python中读取文本文件中的单词
- html - 在 html 元素的样式属性中设置的项目的技术名称是什么?
- android - Room 中的复合键关系
- kotlin - KOTLIN 将字符串转换为泛型类型