简体   繁体   English

在 React 中将外部库从基于类迁移到函数式组件

[英]Moving external library from Class-based to Functional Component in React

I'm using the JExcel javascript library with React.我将 JExcel javascript 库与 React 一起使用。 The documentation outlines the approach with Components, but makes use of ReactDOM.findDOMNode() which I believe has been deprecated.该文档概述了使用组件的方法,但使用了我认为已被弃用的ReactDOM.findDOMNode()

I've tried moving it to a functional component, but whilst it does ostensibly work, there is an issue, in that the React component using the class re-renders about 5 times... and each re-render causes the JExcel element to add another sheet!我已经尝试将它移动到一个功能组件,但是虽然它表面上确实有效,但存在一个问题,因为使用 class 的 React 组件重新渲染大约 5 次......并且每次重新渲染都会导致 JExcel 元素添加另一张纸!

Here's the original example code:这是原始示例代码:

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

Here's my updated version, as a <MyGrid /> component:这是我的更新版本,作为<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);

The problem is that the data is coming via a fetch in a parent component.问题是数据是通过父组件中的获取来的。 I suspect it's down to that, or possible my poor use of useRef .我怀疑是因为这个,或者可能是我对useRef的使用不当。 Is there a better approach?有更好的方法吗?

Okay, with help from Paul @ JExcel here's the trick to get it to work.好的,在 Paul @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>
  )
}

To prevent multiple calls to the external library (jexcel in this case) the trick is to check that (a) the params have been passed into the component, ie in this case options.data.length > 0 , and (b) that we only call the external library the first time we have those params, ie .sheetRef.current.innerHTML - if the sheetRef element has no innerHTML, then it hasn't be initiated, so it must be the first time around.为了防止多次调用外部库(在这种情况下为 jexcel),诀窍是检查(a)参数是否已传递到组件中,即在这种情况下options.data.length > 0和(b)我们只有在我们第一次获得这些参数时才调用外部库,即.sheetRef.current.innerHTML - 如果sheetRef元素没有 innerHTML,那么它还没有被启动,所以它必须是第一次。 Therefore, the call to setMySheet only happens once, and only when all the properties have been passed in.因此,对setMySheet的调用仅发生一次,并且仅在所有属性都已传入时发生。

Here's a Sandbox showing it in action.这是一个沙盒,展示了它的实际应用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将基于 React 类的组件转换为函数式组件? - How to convert a React class-based component into a functional component? 将基于反应类的组件转换为基于功能的组件 - convert react class-based to functional based component 如何将基于类的组件中的函数转换为 React 中的函数式组件? - How can I convert functions in class-based component to functional components in React? 何时在 React 中使用基于类的组件 - When to use class-based component in React 将基于 React class 的组件转换为功能组件 - Converting React class based component to functional component 如何在反应中将同步数据从函数发送到基于类的组件? - How to send synchronous data from a functional to a class based component in react? REACT:我们如何将外部库作为脚本,使用类或功能组件? 可能吗? - REACT: How could we use an external library as a script, into a class or functional component? Is it possible? 将 react 中的功能组件转换为基于 class 的组件 - Conversion of functional components in react into class based component 在 React 的功能组件内创建对外部 class 组件的引用 - Create ref to external class component inside functional component in React 为什么使用无状态功能组件而不是基于类的组件? - Why use stateless functional Components instead of class-based Components?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM