1个以上实例时React-Tabulator组件崩溃

时间:2020-06-08 22:56:27

标签: reactjs react-hooks tabulator react-functional-component

我有一个可以在其中渲染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的单个实例时,这可以正常工作,但是在运行多个实例时,它会崩溃。

结果:

  • 如果将输出到setColumns的动态数组复制并直接应用于ReactTabulator,那么就没有问题-没有卸载
  • 如果将输出到setColumns的动态数组复制并设置为挂钩的初始值,则问题仍然会发生-卸载会发生

虽然我的错误是“ null”(因为始终存在,并且列挂钩具有默认的空数组),但这并没有向我解释。

1 个答案:

答案 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