react-tabulator icon indicating copy to clipboard operation
react-tabulator copied to clipboard

Refactor reactFormatter to use createRoot for React 18 compatibility

Open aleruizj opened this issue 2 years ago • 3 comments

This update modifies the reactFormatter utility to use the createRoot function from 'react-dom/client', ensuring compatibility with React 18's new rendering API. It addresses the warning about ReactDOM.render being deprecated in the new version of React.

By wrapping the JSX element with createRoot, we're able to mount React components within Tabulator's custom formatter without relying on the outdated ReactDOM.render method. This change adheres to the best practices recommended by the React 18 upgrade strategy.

aleruizj avatar Dec 08 '23 09:12 aleruizj

@aleruizj Nice work, but I see a mistake in your code. There is no props passing from tabulator to JSX element.

HolyArseny avatar Jan 25 '24 13:01 HolyArseny

i dont see why return "<div class='formatterCell'></div>"; when you only do cell.getElement().

also you could pass the props just like before. so that you dont break compatiblity.

also notice that there is row height issue with this code (not related to your change specifically but how react and tabulator renderer stuff work i guess)

attached screenshot: before using render(): image

after using root.render(): image

i dont have experience with react i dont know why :)

best result i came up with (that works on my application) was this: (i dont like how to code looks, specially that setTimeout(10mS))


const rootMap = new Map<HTMLElement, Root>();

export function reactFormatterNew(JSX: any) {
  return function customFormatter(
    cell: Tabulator.CellComponent,
    formatterParams: any,
    onRendered: (callback: () => void) => void
  ) {
    const renderFn = () => {
      const cellEl = cell.getElement();
      const existingRoot = rootMap.get(cellEl);
      const CompWithProps = React.cloneElement(JSX, { cell });

      if (existingRoot) {
        existingRoot.render(CompWithProps);

        cell.getRow().normalizeHeight();
      } else if (cellEl) {
        const formatterCell = cellEl.querySelector('.formatterCell');
        if (formatterCell) {
          const root = createRoot(cellEl);
          rootMap.set(cellEl, root);
          root.render(CompWithProps);

          cell.getRow().normalizeHeight();
        }
      }
    };

    onRendered(renderFn);

    setTimeout(() => {
      renderFn();
    }, 10);

    return '<div class="formatterCell"></div>';
  };
}

update: i had to register table.on('rowUpdated', r => r.normalizeHeight()) on my application to make rows update their height.

however, the solution has flaws. tabulator would delete rows and cells, this is not friendly with react 18 as it expects user to call root.unmount(). i tried to register callback ('rowDelete') but it did not work for me. so i downgraded my app to react 17

hsn93 avatar Feb 17 '24 12:02 hsn93

Any movement on this?

dtoxvanilla1991 avatar Feb 21 '24 20:02 dtoxvanilla1991