首页 > 解决方案 > 在 angularjs 应用程序中重用 react 应用程序

问题描述

我想在 angularjs 应用程序中完全重用一个小型反应应用程序。有没有办法达到同样的效果。我已经使用 ngReact 在 Angular js 中加载反应,它工作正常。但是,我如何完全重用应用程序而无需太多更改。

注意:我知道这是一种不好的做法,但由于遗留代码,必须实现它。

任何线索表示赞赏。蒂亚!

标签: reactjsangularjs

解决方案


解决方案实施

包.json

"dependencies": {
    ...
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    ...
  },
"devDependencies": {
    ...
    "@types/react": "^16.9.17",
    "@types/react-dom": "^16.9.4",
    ...
}

tsconfig.json

{
     ... 
     "jsx": "react",
     ...
}

包装器组件

包装器负责检测更改并重新渲染被包装的 React 组件,使其 props 生效,最终在包装器被销毁时卸载被包装的组件。

MyReactComponentWrapper.tsx

import {
  AfterViewInit,
  Component,
  ElementRef,
  EventEmitter,
  Input,
  OnChanges,
  OnDestroy,
  Output,
  SimpleChanges,
  ViewChild,
  ViewEncapsulation
} from '@angular/core';
import { MyReactComponent } from 'src/components/my-react-component/MyReactComponent';
import * as React from 'react';

import * as ReactDOM from 'react-dom';

const containerElementName = 'myReactComponentContainer';

@Component({
  selector: 'app-my-component',
  template: `<span #${containerElementName}></span>`,
  styleUrls: ['./MyReactComponent.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class MyComponentWrapperComponent implements OnChanges, OnDestroy, AfterViewInit {
  @ViewChild(containerElementName, {static: false}) containerRef: ElementRef;

  @Input() public counter = 10;
  @Output() public componentClick = new EventEmitter<void>();

  constructor() {
    this.handleDivClicked = this.handleDivClicked.bind(this);
  }

  public handleDivClicked() {
    if (this.componentClick) {
      this.componentClick.emit();
      this.render();
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    this.render();
  }

  ngAfterViewInit() {
    this.render();
  }

  ngOnDestroy() {
    ReactDOM.unmountComponentAtNode(this.containerRef.nativeElement);
  }

  private render() {
    const {counter} = this;

    ReactDOM.render(<div className={'i-am-classy'}>
      <MyReactComponent counter={counter} onClick={this.handleDivClicked}/>
    </div>, this.containerRef.nativeElement);
  }
}

MyReactComponent.tsx

import * as React from 'react';
import { FunctionComponent, useEffect, useRef, useState } from 'react';

import './MyReactComponent.scss';

export interface IMyComponentProps {
  counter: number;
  onClick?: () => void;
}

export const MyReactComponent: FunctionComponent<IMyComponentProps> = (props: IMyComponentProps) => {

  const timerHandle = useRef<number | null>(null);
  const [stateCounter, setStateCounter] = useState(42);

  useEffect(() => {
    timerHandle.current = +setInterval(() => {
      setStateCounter(stateCounter + 1);
    }, 2500);

    return () => {
      if (timerHandle.current) {
        clearInterval(timerHandle.current);
        timerHandle.current = null;
      }
    };
  });

  const {counter: propsCounter, onClick} = props;

  const handleClick = () => {
    if (onClick) {
      onClick();
    }
  };

  return <div className={`my-graph-component`}>
    <div className={'comp-props'}>Props counter: {propsCounter}
      <span onClick={handleClick}
            className={'increase-button'}>click to increase</span>
    </div>
    <div className={'comp-state'}>State counter: {stateCounter}</div>
  </div>;
};


推荐阅读