我有一个可以在其中渲染react-tabulator组件的组件。
如果我尝试渲染此组件的2个实例,那么当包装器组件被目标应用程序多次实例化时,Tabulator会在Storybook中引发错误并返回相同的错误,但对于“ setColumns”而不是“ destroy”。 / p>
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)
我在不同的环境中尝试过此操作,例如在Storybook中:
export const BasicTable = () => (
<>
<Table isEditable={true} data={data} schema={schemaSmall} />
<Table isEditable={true} data={data} schema={schemaSmall} />
</>
);
架构或数据没有问题,因为如果只有1个实例,则两者都可以正确呈现。
wrapper组件是一个React功能组件,它利用useRef挂钩来引用React-Tabulator实例,这两个挂钩ref是否可能发生冲突?
这来自一个相当大的组件,但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理论,并为每个实例提供不同的参考:
const ref = useRef();
const ref1 = useRef();
但是这仍然不起作用,并且对一个Table.jsx实例抛出了相同的错误。 (而且无论如何都不会解决该问题,因为需要未知数量的实例化。)
更新2: 在帮助下,我设法缩小了问题的范围。实例化Table.jsx时,会向其传递一个模式和数据,该模式在内部进行解析以构建一个列数组,并应用于一个钩子:
const [columns, setColumns] = useState([])
然后在ReactTabulator的声明中引用列。
在运行Table.jsx的单个实例时,这可以正常工作,但是在运行多个实例时,它会崩溃。
结果:
虽然我的错误是“ null”(因为始终存在,并且列挂钩具有默认的空数组),但这并没有向我解释。
答案 0 :(得分:1)
首先,我认为ref
道具没有任何作用。我查看了react-tabulator的源代码,它只是未使用的。因此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 />
。如果您看到注销的“卸载”消息,那么我的理论就被证明是正确的。
更新:
我忘了提到上述理论的理由。来自this line的原始错误TypeError: Cannot read property 'destroy' of null
来源:
componentWillUnmount() {
this.table.destroy();
}
之所以会这样,是因为react-tabulator
lib在内部使用了async componentDidMount()
生命周期,在生命周期内await
用于React的异步渲染事件之前,最终实例化了{{ 1}}并分配给Tabulator
。
通常,该异步事件应该立即解决,因此应该在this.table
之前为this.table
分配一个值。但是,在您的情况下,“闪存卸载”发生在该异步事件可以解决之前,因此,componentWillUnmount()
在调用this.table
时是null
,因此会出错。
有关源代码中的详细跟踪this line。