Перемещение внешней библиотеки из класса на функциональный компонент в React - PullRequest
1 голос
/ 03 мая 2020

Я использую библиотеку JExcel javascript с React. Документация описывает подход с использованием компонентов, но использует ReactDOM.findDOMNode(), который, я считаю, устарел.

Я пытался переместить его в функциональный компонент, но хотя он якобы работает, есть проблема , в том, что компонент React, использующий класс, выполняет рендеринг примерно 5 раз ... и каждый повторный рендеринг заставляет элемент JExcel добавлять другой лист!

Вот исходный код примера:

import React from "react";
import ReactDOM from "react-dom";
import jexcel from "jexcel-pro";

import "./styles.css";
import "../node_modules/jexcel-pro/dist/jexcel.css";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.options = props.options;
  }

  componentDidMount = function() {
    this.el = jexcel(ReactDOM.findDOMNode(this).children[0], this.options);
  };

  addRow = function() {
    this.el.insertRow();
  };

  render() {
    return (
      <div>
        <div />
        <br />
        <input
          type="button"
          value="Add new row"
          onClick={() => this.addRow()}
        />
      </div>
    );
  }
}

const options = {
    data: [
      [123, 412],
      [null, 32, 43],
      [null, null, 41, null, 54],
      [null, 123, null, 64, 23],
      [null, null, 41, 23, 123]
    ],
    minDimensions: [5, 5],
    freezeColumns: 2,
    allowComments: true,
    tableOverflow: true,
    tabs: false,
    allowCreateTabs: false,
    tableWidth: "100%"
  };

const rootElement = document.getElementById("root");
ReactDOM.render(<App options={options} />, rootElement);

Вот моя обновленная версия как <MyGrid /> компонент:

import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
import jexcel from "jexcel-pro";

import "./styles.css";
import "../node_modules/jexcel-pro/dist/jexcel.css";

function App(props) {
  var options = {
    data: [
      [123, 412],
      [null, 32, 43],
      [null, null, 41, null, 54],
      [null, 123, null, 64, 23],
      [null, null, 41, 23, 123]
    ],
    minDimensions: [5, 5],
    freezeColumns: 2,
    allowComments: true,
    tableOverflow: true,
    tabs: false,
    allowCreateTabs: false,
    tableWidth: "100%"
  };
  return <MyGrid options={options} />;
}

function MyGrid(props) {
  const { options } = props;
  const sheetRef = useRef(null);
  const [mySheet, setMySheet] = useState(null);

  useEffect(() => {
    setMySheet(jexcel(sheetRef.current, options));
  }, [options]);

  function addRow() {
    mySheet.insertRow();
  }

  return (
    <div>
      <div ref={sheetRef} />
      <br />
      <input type="button" value="Add new row" onClick={() => addRow()} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Проблема в том, что данные поступают через выборку в родительский компонент. Я подозреваю, что это из-за этого, или, возможно, мое плохое использование useRef. Есть ли лучший подход?

1 Ответ

0 голосов
/ 04 мая 2020

Хорошо, с помощью Пола @ JExcel вот трюк, чтобы заставить его работать.

import React, { useRef, useState, useEffect } from 'react'
import jexcel from 'jexcel-pro'

export default function MyGrid(props) {
  const { options } = props
  const sheetRef = useRef(null)
  const [mySheet, setMySheet] = useState(null)

  useEffect(() => {
    // here's the magic...
    if (!sheetRef.current.innerHTML && options.data.length > 0) {
      setMySheet( jexcel(sheetRef.current, options))
    }
    // clean-up function
    return function removeSheet() {
      sheetRef.current.remove();
    }
  }, [options])

  function addRow() {
    mySheet.insertRow()
  }

  return (
    <div>
      <div ref={sheetRef} />
      <br />
      <input type="button" value="Add new row" onClick={() => addRow()} />
    </div>
  )
}

Чтобы предотвратить множественные вызовы во внешнюю библиотеку (в данном случае jexcel), хитрость заключается в проверке того, что (a) параметры переданы в компонент, т.е. в этом случае options.data.length > 0, и (b) что мы вызываем внешнюю библиотеку только при первом получении этих параметров, то есть !sheetRef.current.innerHTML - если элемент sheetRef не имеет внутреннего HTML, поэтому он не был инициирован, поэтому он должен быть в первый раз. Поэтому вызов setMySheet происходит только один раз и только после того, как все свойства были переданы.

Вот Песочница , показывающая его в действии.

...