将基于 Class 的外部库移动到 React 中的功能组件
Moving external library from Class-based to Functional Component in React
我将 JExcel javascript 库与 React 一起使用。该文档概述了使用组件的方法,但使用了我认为已被弃用的 ReactDOM.findDOMNode()
。
我已经尝试将它移动到一个功能组件,但虽然它表面上可以工作,但存在一个问题,因为使用 class 的 React 组件重新渲染了大约 5 次......并且每次重新渲染都会导致 JExcel 元素添加另一个 sheet!
这是原始示例代码:
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
的使用不当。有没有更好的方法?
好的,在 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>
)
}
为了防止多次调用外部库(在本例中为 jexcel),诀窍是检查 (a) 参数是否已传递到组件中,即在本例中 options.data.length > 0
,以及 (b ) 我们只在第一次有这些参数时调用外部库,即 !sheetRef.current.innerHTML
- 如果 sheetRef
元素没有 innerHTML,那么它还没有被启动,所以它必须是第一次大约。因此,对 setMySheet
的调用只会发生一次,并且只有在所有属性都已传入时才会发生。
这里是 Sandbox 展示它的实际效果。
我将 JExcel javascript 库与 React 一起使用。该文档概述了使用组件的方法,但使用了我认为已被弃用的 ReactDOM.findDOMNode()
。
我已经尝试将它移动到一个功能组件,但虽然它表面上可以工作,但存在一个问题,因为使用 class 的 React 组件重新渲染了大约 5 次......并且每次重新渲染都会导致 JExcel 元素添加另一个 sheet!
这是原始示例代码:
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
的使用不当。有没有更好的方法?
好的,在 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>
)
}
为了防止多次调用外部库(在本例中为 jexcel),诀窍是检查 (a) 参数是否已传递到组件中,即在本例中 options.data.length > 0
,以及 (b ) 我们只在第一次有这些参数时调用外部库,即 !sheetRef.current.innerHTML
- 如果 sheetRef
元素没有 innerHTML,那么它还没有被启动,所以它必须是第一次大约。因此,对 setMySheet
的调用只会发生一次,并且只有在所有属性都已传入时才会发生。
这里是 Sandbox 展示它的实际效果。