【问题标题】:React-Tabulator component crashing when more than 1 instance超过 1 个实例时 React-Tabulator 组件崩溃
【发布时间】:2020-09-28 00:45:59
【问题描述】:

我有一个组件,它在其中呈现一个 react-tabulator 组件。

如果我尝试渲染此组件的 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)

我已经在不同的环境中尝试过,例如在 Storybook 中:

export const BasicTable = () => (
    <>
        <Table isEditable={true} data={data} schema={schemaSmall} />
        <Table isEditable={true} data={data} schema={schemaSmall} />
    </>
);

架构或数据没有问题,因为如果只有 1 个实例,两者都会正确呈现。

包装器组件是一个 React 功能组件,它利用 useRef 钩子来引用 React-Tabulator 实例,这两个钩子引用是否可能发生冲突?

这是来自一个相当大的组件,但 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”,但这并没有向我解释,因为它始终存在,并且列挂钩具有默认的空数组。

【问题讨论】:

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


    【解决方案1】:

    首先,我认为ref props 没有任何作用。我查看了 react-tabulator 的源代码,它只是未使用。所以useRef() 不应该造成任何伤害。

    我怀疑问题出在其他地方。我的理论是在您的应用程序内部某处发生了“闪存卸载”。 “flash umount”是指一个组件被挂载,然后由于某种原因立即被卸载。

    为了测试我的理论,尝试用这个虚拟组件替换&lt;ReactTabulator /&gt;

    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>
    }
    

    尝试渲染此&lt;Dummy /&gt; 组件的多个实例来代替&lt;ReactTabulator /&gt;。如果您观察到“已卸载”消息已注销,那么我的理论被证明是正确的。


    更新: 我忘记提及提出上述理论的原因。原错误TypeError: Cannot read property 'destroy' of null 来自this line

    componentWillUnmount() {
      this.table.destroy();
    }
    

    这是因为react-tabulator lib 内部使用了一个async componentDidMount() 生命周期,其中await 用于来自React 的异步渲染事件之前 它最终实例化Tabulator 并分配给@987654335 @。

    通常,异步事件应该立即解决,因此应该在 componentWillUnmount() 之前为 this.table 分配一个值。但是,在您的情况下,“闪存卸载”发生在异步事件可以解决之前,因此当调用 componentWillUnmount() 时,this.tablenull,因此出现错误。

    详细跟踪源代码中的this line

    【讨论】:

    • 谢谢。所以。我在 Table 组件中将 ReactTablulator 换成 Dummy - 对于多个实例都运行良好(没有卸载)然后我返回带有 Dummy 的 ReactTabulator 以及空数据并且除了列之外没有任何道具 - 运行多个实例并且没有问题或卸载。然后,我使用数据运行相同的设置并得到无法读取 XXX of null 错误,并且两个 Dummy 实例都被跟踪卸载
    • 仅供参考,我在票证中添加了更多位 - 您绝对正确,当我们有多个实例时会发生卸载,但只有一个实例时不会发生。
    • @Rich 我仍然没有在您发布的代码中看到实际问题。我相信它在您的组件层次结构中甚至更高。但我没有更多的想法。
    • 您的解释似乎给出了为什么在某些情况下我在 componentDidMount 中引用 React-Tabulator 时得到 null 值的原因,例如当 this.tableref.current.table.setColumns 这样的代码时(coldef) 被执行。但奇怪的是,这也影响了我简单地引用 html 标签的简单虚拟 ref,即它包含 null。如果没有上述代码,我对 React-Tabulator 和 dummy ref 的引用都包含 componentDidMount 中的预期值。
    • 但是,这种行为最近才发生,因为我的硬盘损坏了,我不得不重新安装所有东西。以前,我的代码按预期运行。所以我不确定究竟是什么导致了这种意外行为。
    猜你喜欢
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-29
    • 2012-02-27
    • 1970-01-01
    • 2019-12-07
    • 2017-04-29
    相关资源
    最近更新 更多