建立无需build的react单页面应用SPA框架(2)

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(
    
  );
}

你可能感兴趣的:(Javascript,react.js,前端,前端框架,easyui,babel)