Реакция повторного рендеринга после изменения модели данных с угловой директивы - PullRequest
0 голосов
/ 19 октября 2018

Контекст:

  1. По аналогии со следующим документом, я работаю в приложении, которое включает в себя некоторую версию React в Angular 1.5.9:https://codeburst.io/how-to-hook-reactjs-to-your-existing-angularjs-1-x-app-5ab1ac59c0c1
  2. Используя это встраивание React, я создаю Gantt способом, аналогичным описанному в следующем руководстве из DHTMLXGantt, сторонней библиотеки, которую я использую для генерации Gantt: https://dhtmlx.com/blog/create-react-gantt-chart-component-dhtmlxgantt/

Предположение:

  1. Будет модель данных TypeScript + Redux, содержащая необходимую информацию для задач Ганта, и я передам эти данныемодели в соответствующую угловую директиву с односторонним связыванием данных в приведенном ниже коде gantt.directive.jsx ниже, используя <.

Проблема:

Учитывая мой приведенный ниже код, оптимизируя для (1) количества строк изменения кода, (2) следования передовым методикам Angular и React и (3) эффективности, как я должен изменить свой код, чтобы (i)) когда модель данных задач меняется, мыповторно выполнить рендеринг Ганта, независимо от того, останется ли свойство масштаба одинаковым (т. е. сейчас, я думаю, shouldComponentUpdate предотвращает это обновление, если свойство масштабирования остается прежним), и (ii) я не удаляю дух пред-существующая логика после этого повторного рендеринга (например, если после этого повторного рендеринга пользователь меняет масштаб с «Дней» на «Дни», мы должны придерживаться «shouldComponentUpdate» внутри Gantt.jsx).


Файл 1: gantt.directive.jsx

/* global angular */

import React from 'react';
import ReactDOM from 'react-dom';
import GanttWrapper from './GanttWrapper';

/*
  Inspiration from:
    - https://codeburst.io/how-to-hook-reactjs-to-your-existing-angularjs-1-x-app-5ab1ac59c0c1
*/

(() => {
  const componentId = 'alice-gantt';
  angular
    .module('app.prepare')
    .directive('gantt', [() => ({
      template: `<div id=${componentId}>hi</div>`,
      restrict: 'E',
      scope: { tasks: '<', onGanttChange: '&' },
      link: (scope, element, attributes) => {
        const { vm, solution, tasks } = scope;
        const reactRoot = document.getElementById(componentId);

        scope.$watch('tasks', function(newVal, oldV) {
          if (angular.isDefined(newVal)) {
            ReactDOM.render(
              <GanttWrapper 
                tasks={scope.tasks} 
                onGanttChange={scope.onGanttChange}
                />
              , reactRoot);
          }
        }, true);

        scope.$on('$destroy', () => {
          ReactDOM.unmountComponentAtNode(reactRoot);
        });
      },
    })]);
})();

Файл 2: GanttWrapper.jsx

import React, { Component } from 'react';
import Gantt from './Gantt';
import GanttToolbar from './GanttToolbar';

/*
    Inspiration from:
        - https://dhtmlx.com/blog/create-react-gantt-chart-component-dhtmlxgantt/
        - https://codeburst.io/how-to-hook-reactjs-to-your-existing-angularjs-1-x-app-5ab1ac59c0c1
*/

export default class GanttWrapper extends Component {

  constructor(props) {
    super(props);
    this.state = {
        currentZoom: 'Days',
    }
    this.handleZoomChange = this.handleZoomChange.bind(this);
  }

  handleZoomChange(zoom /*string*/) {
    this.setState({
        currentZoom: zoom
    });
  }

  render() {
    return (
      <div>
        <GanttToolbar
          zoom={this.state.currentZoom}
          onZoomChange={this.handleZoomChange}
        />
        <div className="gantt-container">
          <Gantt
           tasks={this.props.tasks}
           onGanttChange={this.props.onGanttChange}
           zoom={this.state.currentZoom}
          />
        </div>
      </div>
    );
  }
}

Файл 3: GanttToolbar.jsx

/*
  Inspiration from:
    - https://raw.githubusercontent.com/DHTMLX/react-gantt-demo/master/src/Toolbar.js
*/

import React, { Component } from 'react';

export default class Toolbar extends Component {
  constructor(props) {
    super(props);
    this.handleZoomChange = this.handleZoomChange.bind(this);
  }

  handleZoomChange(e) {
    if(this.props.onZoomChange){
      this.props.onZoomChange(e.target.value)
    }
  }

  render() {
    let zoomRadios = ['Hours', 'Days', 'Months'].map((value) => {
      let isActive = this.props.zoom === value;
      return (
        <label key={value} className={`radio-label ${isActive ? 'radio-label-active': ''}`}>
          <input type='radio'
             checked={isActive}
             onChange={this.handleZoomChange}
             value={value}/>
          {value}
        </label>
      );
    });

    return (
      <div className="zoom-bar">
        <b>Zooming: </b>
          {zoomRadios}
      </div>
    );
  }
}

Файл 4: Gantt.js

/*global gantt */

// inspiration from https://dhtmlx.com/blog/create-react-gantt-chart-component-dhtmlxgantt/

import React, { Component } from 'react';
import 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';

export default class Gantt extends Component {

  initGanttEvents() {
    if(gantt.ganttEventsInitialized){
      return;
    }
    gantt.ganttEventsInitialized = true;

    gantt.attachEvent('onAfterTaskAdd', (id, task) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'inserted', task);
      }
    });

    gantt.attachEvent('onAfterTaskUpdate', (id, task) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'updated', task);
      }
    });

    gantt.attachEvent('onAfterTaskDelete', (id) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'deleted');
      }
    });

    gantt.attachEvent('onAfterLinkAdd', (id, link) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'inserted', link);
      }
    });

    gantt.attachEvent('onAfterLinkUpdate', (id, link) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'updated', link);
      }
    });

    gantt.attachEvent('onAfterLinkDelete', (id, link) => {
      if(this.props.onGanttChanged) {
        this.props.onGanttChanged(id, 'deleted');
      }
    });
  }

  componentDidMount() {
    this.initGanttEvents();
    gantt.init(this.ganttContainer);
    gantt.parse(this.props.tasks);
  }

  // prevent render call
  shouldComponentUpdate(nextProps) {
    return this.props.zoom !== nextProps.zoom
  }

  // invoked whenever state change
  componentDidUpdate() {
    gantt.render();
  }

  render() {
    this.setZoom(this.props.zoom);
    return (
      <div
        ref={(input) => {this.ganttContainer = input}}
        style={{width: '100%', height: '100%'}}
      ></div>
    );
  }

  setZoom(value){
    switch (value){
      case 'Hours':
        gantt.config.scale_unit = 'day';
        gantt.config.date_scale = '%d %M';

        gantt.config.scale_height = 60;
        gantt.config.min_column_width = 30;
        gantt.config.subscales = [
          {unit:'hour', step:1, date:'%H'}
        ];
        break;
      case 'Days':
        gantt.config.min_column_width = 70;
        gantt.config.scale_unit = "week";
        gantt.config.date_scale = "#%W";
        gantt.config.subscales = [
          {unit: "day", step: 1, date: "%d %M"}
        ];
        gantt.config.scale_height = 60;
        break;
      case 'Months':
        gantt.config.min_column_width = 70;
        gantt.config.scale_unit = "month";
        gantt.config.date_scale = "%F";
        gantt.config.scale_height = 60;
        gantt.config.subscales = [
          {unit:"week", step:1, date:"#%W"}
        ];
        break;
      default:
        break;
    }
  }
}

Заранее благодарим за помощь,на самом деле.

...