我有一个组件,它在其中渲染了一个反应制表器组件。
如果我尝试渲染此组件的 2 个实例,则 Tabulator 在 Storybook 中抛出错误,并且当包装器组件与目标应用程序多次实例化时,对于"setColumns"而不是"destroy",也会引发相同的错误。
TypeError: Cannot read property 'destroy' of null
at default_1.push../node_modules/react-tabulator/lib/ReactTabulator.js.default_1.componentWillUnmount (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:231022:20)
at callComponentWillUnmountWithTimer (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:214466:12)
at HTMLUnknownElement.callCallback (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:195074:14)
at Object.invokeGuardedCallbackDev (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:195123:16)
at invokeGuardedCallback (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:195178:31)
at safelyCallComponentWillUnmount (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:214473:5)
at commitUnmount (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:214995:11)
at commitNestedUnmounts (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:215049:5)
at unmountHostComponents (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:215329:7)
at commitDeletion (http://localhost:6006/vendors~main.72ccccb1e4a331ed682e.bundle.js:215386:5)
我已经在不同的环境中尝试过这个,例如在故事书中:
export const BasicTable = () => (
<>
<Table isEditable={true} data={data} schema={schemaSmall} />
<Table isEditable={true} data={data} schema={schemaSmall} />
</>
);
架构或数据没有问题,因为如果只有一个实例,两者都会正确呈现。
包装器组件是一个 React 功能组件,它利用 useRef 钩子来引用 React-Tabulator 实例,2 个钩子引用是否有可能冲突?
这是来自一个相当大的组件,但来自Table.jsx的(IMO(相关部分是:
import 'react-tabulator/lib/styles.css';
import 'react-tabulator/lib/css/tabulator.min.css';
import { reactFormatter, ReactTabulator } from 'react-tabulator';
const Table = ({
data,
schema,
}) => {
const ref = useRef();
const [tableColumns, setTableColumns] = useState([]);
// is only running once per instance
useEffect(() => {
// builds an array of columns
setTableColumns(array);
}, [amEditing]
const options = {
history: true,
layoutColumnsOnNewData: true,
virtualDom: false,
};
return (
<StyledWrapper style={{ width }}>
<ReactTabulator
ref={ref}
columns={tableColumns}
data={[]}
options={options}
/>
</StyledWrapper>
);
};
结果:如果以单数形式使用 Table.jsx 组件,则在任一环境中都没有错误。
结果:Table.jsx 组件与原始 React-Tabulator 组件的多个实例一起工作。
如果是useRef钩子,那么有没有办法解决这个问题? 我看不出此失败的任何其他可能原因
更新 1:我通过在 Table.jsx 组件中复制 react-tabulator 实例化来测试 useRef 理论,并为每个组件提供了不同的 ref:
const ref = useRef();
const ref1 = useRef();
但这仍然不起作用,并在一个 Table.jsx 实例中抛出了相同的错误。(无论如何都不会解决问题,因为需要未知数量的实例化(。
更新2:在帮助下,我设法缩小了问题的范围。当 Table.jsx 实例化时,它会传递一个架构和数据,架构在内部解析以构建一个应用于钩子的列数组:
const [columns, setColumns] = useState([])
然后在 ReactTabulator 的声明中引用列。
当运行 Table.jsx 的单个实例时,这完全可以正常工作,但是当运行多个实例时它会崩溃。
结果:
- 如果输出到 setColumns 的动态数组被复制并直接应用于 ReactTabulator,则没有问题 - 没有卸载
- 如果输出到 setColumns 的动态数组被复制并设置为钩子的初始值,则问题仍然发生 - 卸载发生
这并不能向我解释,尽管我的错误是"null",因为始终存在,并且列钩子有一个默认的空数组。
首先,我不认为ref
道具有任何影响。我查看了反应制表器的源代码,它只是未使用。所以useRef()
不应该造成任何伤害。
我怀疑问题出在别处。我的理论是,在您的应用程序内部的某个地方发生了"闪光卸载"。通过"闪存卸载",我的意思是组件被安装,然后由于某种原因立即卸载。
为了验证我的理论,请尝试用这个虚拟组件替换<ReactTabulator />
:
function Dummy() {
const id = useRef(Math.random()).current
console.log(`rendering dummy_${id}`)
useEffect(() => {
console.log(`mounted dummy_${id}`)
return () => {
console.log(`HEADS UP! Unmounted dummy_${id}`)
}
}, [])
return <div>{id}</div>
}
尝试渲染此<Dummy />
组件的多个实例来代替<ReactTabulator />
。如果您观察到"卸载"消息已注销,那么我的理论被证明是正确的。
更新:我忘了提到提出上述理论的理由。原始错误TypeError: Cannot read property 'destroy' of null
来自此行:
componentWillUnmount() {
this.table.destroy();
}
发生这种情况是因为 lib 内部使用react-tabulator
生命周期async componentDidMount()
生命周期,在该生命周期内,它会在最终实例化Tabulator
并分配给this.table
之前await
来自 React 的异步渲染事件。
通常,该异步事件应立即解决,因此应在componentWillUnmount()
之前this.table
分配一个值。但是,在您的情况下,"闪存卸载"发生在异步事件可以解析之前,因此this.table
在调用componentWillUnmount()
时null
,因此错误。
有关详细信息,请在源代码中跟踪此行。