Skip to content

Allow ReorderCell to work in React v19 #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 27, 2025

Conversation

pradeepnschrodinger
Copy link
Collaborator

@pradeepnschrodinger pradeepnschrodinger commented Mar 31, 2025

Issue

The <ReorderCell /> plugin component doesn't work correctly in React v19.
This is because it uses the ReactDOM.render API, and this is removed in React v19.

Fix

I'm adding in a new prop named __react19RootCreator which allows users to pass in the createRoot API so that <ReorderCell /> works as expected.
The prop is already marked as deprecated in the docs, and prefixed with__ to make it apparent that this is more of a hack/temporary workaround.
We'll remove it in FDT v3 since we'll probably only support React 18 and above.

Example usage:

import { createRoot } from 'react-dom/client';

...

const reorderCell = (
  <Plugins.ReorderCell
    __react19RootCreator={createRoot}
  >
    {cellContents}
  </Plugins.ReorderCell> 

Other fixes?

My first thought to fix this was to do feature detection by checking if ReactDOM.render or ReactDOM.createRoot exists at runtime.
Unfortunately, the replacement ReactDOM.createRoot is in the new react-dom/client package which is unavailable in React v17.
So the following snippet doesn't work in ESM environment in browsers:

import ReactDOM from 'react-dom' // for react v17
// the next line throws an error for clients using react v17 because the package doesn't exist
import ReactDOMClient from 'react-dom/client' // for react v19

I tried using async imports (import('ReactDOM/client').then) and commonJS imports (const ReactDOM = require('ReactDOM/client') so that we can do something similar to "optional" imports.
But these don't work without requiring the client to add config changes to their bundler/devserver (eg: storybook's react-dom shim).

Another approach I can think of is publishing the plugins as a separate package.
This'll allow us to publish different versions of the plugin for different versions of React.
This is a lot of work for FDT v2 though, and I don't think we can do this cleanly without breaking changes 🤔 .

Yet another approach is to extend our FixedDataTableAPI to allow plugins to register renderers into FDT, which FDT will then render outside the virtualization layer.
This way reoreder cell's contents won't be lost even if the original column goes outside the viewport, solving the original use case for ReactDOM.render.
This seems like a lot of work for a very specific case though.

History of ReorderCell

When we first implemented the ReorderCell plugin we ran into a nasty edge case where if the user dragged the ReorderCell far away from it's original column, the original/parent cell renderer gets unmounted because it's no longer within the visible viewport (because of FDT's virtualization).
This causes the dragged cell to also disappear.

We tried using React Portals to render the dragged cell contents.
But the portals also disappears since it's rendered directly by the parent cell. 😔

We rendered into a separate React tree via ReactDOM.render to fix this specific issue.

Motivation and Context

Fixes #743.

How Has This Been Tested?

Tested the ReorderCell plugin and verified reordering works as expected.
In React v17, there's no additional changes required.
In React v19, reordering works if we pass in the new __react19RootCreator prop.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.

if (this.props.__useReactRoot) {
this.dragContainer.root.unmount();
} else {
ReactDOM.unmountComponentAtNode(this.dragContainer);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unmountComponentAtNode also doesn't exist anymore in React v19.

Copy link

@juzerzarif juzerzarif left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a decent middle ground to avoid a breaking change. I like the idea of registering a renderer but like you said unless we plan on supporting more than just DOM rendering (like react-three-fiber or something) it might just make more sense to break the peer dependency in a future major version.

*
* @deprecated This'll be removed in future major version updates of FDT.
*/
__useReactRoot: PropTypes.func,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe we just call this something very explicit like __react19RootCreator so it's clear we're asking you to just pass in the createRoot API directly

@pradeepnschrodinger pradeepnschrodinger merged commit fb6c370 into master Apr 27, 2025
8 checks passed
@pradeepnschrodinger pradeepnschrodinger deleted the FixReorderingForReactv19 branch April 27, 2025 22:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ReorderCell not working in React 19 due to ReactDOM.render no longer existing
2 participants