react-18.1.0,rc-easyui-1.2.9,babel-7.17.11
SPA还要处理的问题:
(一)tabs切换事件通知
tabs切换时,自己的框架需要处理组件的生命周期,要有active/deactive,让组件能知道何时创建或清除一些资源的使用,比如setInterval/clearInterval。
赋予active/deactive事件通知,在tabs元件的onTabSelect/onTabUnselect事件处理就行了。如何通知?分两种情况:
(1)类式组件,让它定义一个function foil_onStateChanged(state)函数来接收。
page.likeButton.jsx
class Com_LikeButton extends React.Component {
constructor(props) {
console.log('likebutton constructor');
super(props);
this.state = { liked: false };
}
foil_onStateChanged(state){
console.log('likebutton foil_onStateChanged',state);
}
render() {
console.log('likeButton render',this.props);
if (this.state.liked) {
console.log(acroML.browserEngine.LCID);
return t('&File');
}
return (
);
}
}
export default Com_LikeButton;
(2)函数式组件,通过props参数传递。
page.timer.jsx
export default function COM_timer(props){
console.log('page timer function:',props.foil.state);
let [time,setTime]=React.useState(0);
function getNow(){
return time;
}
//timerID不参与渲染,用useRef
let timerID=React.useRef(null);
console.log('timerID:',timerID.current);
if (props.foil.state=='create' || props.foil.state=='active'){
if (timerID.current==null){
console.log('start timer')
timerID.current=setInterval(function(){
console.log('timer:',time);
time++;
setTime(time);
},1000);
}
}
else if (props.foil.state=='deactive'||props.foil.state=='destroy'){
if (timerID.current!=null){
console.log('clear timerID:',timerID.current);
clearInterval(timerID.current);
timerID.current=null;
}
}
return(
{t('&File')}
{getNow()}
)
}
虽然类组件有componentDidMount/componentWillUnmount两个事件来判断组件创建和销毁,但是函数式组件没有。如果框架要统一事件,最好把create/destroy事件也加进去。create可以在异步组件的componentDidMount处理,destroy就不能在动态元件的componentWillUnmount处理了,甚至不能在tabs的onTabClose事件处理,来不及了,虽然类组件可以,但函数式组件不会触发渲染重调用。
com.bizCom.jsx
import { Suspense,Component } from 'react';
class Com_bizCom extends React.Component {
constructor(props) {
console.log('Com_bizCom constructor');
super(props);
props.foil.onStateChanged=this.foil_onStateChanged.bind(this);
this.state={
foil:{
state:'create'
}
}
}
shouldComponentUpdate(nextProps, nextState) {
//console.log(nextProps);
//文件相同时不要再渲染,LCID改变后必须重渲染
//if (nextProps.file && (nextProps.file === this.props.file)) return false;
return true;
}
foil_onStateChanged(state){
console.log('bizCom foil_onStateChanged',state);
console.log(this.com);
if (this.com.ref){
//React.Component类组件可以通过函数通知状态
if (this.com.ref.current.foil_onStateChanged){
this.com.ref.current.foil_onStateChanged(state);
}
}
else{
//函数式组件只能通过proprs传递状态,然后bizCOM重渲染
if (this.state.foil.state!=state){
this.state.foil.state=state;
this.com.props.foil.state=state;
this.setState(this.state);
}
}
}
componentDidMount(){
if (this.com.ref){
//只需要组件元件通知一下create状态,函数元件第一渲染已经把create带到props.foil.state
this.com.ref.current.foil_onStateChanged('create');
}
}
componentWillUnmount(){
let self=this;
console.log('bizCom componentWillUnmount',this.com.ref);
//不在这里处理子函数式组件的销毁通知,来不及了,子函数式组件不会调用渲染
//在easyui tab关闭前处理
}
render() {
let self=this;
console.log('Com_bizCom render',this.props);
let file=this.props.file;
if (!file) return null;
/*
let Com=React.lazy(function(){
import函数不能加载jsx
return import(file);
});
return( )
*/
let obj=window.require(file);
//console.log(obj);
if (obj.__esModule===true) obj= obj.default;
// console.log(typeof obj);
// console.log(obj.prototype);
console.log(self.com);
let ops=null;
if (self.com){
ops=self.com.props;
}
else{
ops={foil:{state:'create'}};
if (obj.prototype && obj.prototype.isReactComponent){
//类组件才有ref,函数式组件不能有ref
ops.ref=React.createRef();
}
}
let com=React.createElement(obj,ops);
self.com=com;
return com;
}
}
export default Com_bizCom;
要在tabs的panel关闭前处理,查找easyui tabs源码,找到handleTabClose函数,hook一下:
com.right.jsx
............
componentDidMount(){
let self=this;
console.log('right componentDidMount');
console.log(this.ref_tabs.current.handleTabClose);
//hook handleTabClose这个函数,在关闭panel前通知到bizCom里面的原件要销毁了做一些清理工作,比如清除timer
let fn=this.ref_tabs.current.handleTabClose;
this.ref_tabs.current.handleTabClose=function(panel){
console.log('handleTabClose',panel);
let bizCom=self.getBizCom(panel);
bizCom.props.foil.onStateChanged('destroy');
//必须用异步,否则子函数式组件不会被调用刷新
setTimeout(function(){
fn.call(self.ref_tabs.current,panel);
}, 0);
}
}
(二)主界面切换显示的语言
只要在根原件把LCID设置为响应式,改变时,tabs各个组件会刷新。
com.root.jsx
import Com_Main from './com.main.jsx';
// import Com_acroMLStub from './com.acroML.stub.jsx';
export default function COM_Root(){
console.log('root',acroML.browserEngine.LCID);
let [LCID,setLCID]=React.useState(acroML.browserEngine.LCID);
acroML.browserEngine.switchLanguage=function(){
//console.log(acroML.browserEngine.LCID);
setLCID(acroML.browserEngine.LCID);
//console.log(LCID);
}
//
return(
);
}