[英]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
的调用仅发生一次,并且仅在所有属性都已传入时发生。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.