Skip to content
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

[React 19] findDOMNode is removed, with no suitable replacement for text nodes #31836

Closed
smoores-dev opened this issue Dec 18, 2024 · 2 comments
Labels

Comments

@smoores-dev
Copy link

Summary

In React 18 and below, the only way to obtain a reference to a text node rendered by a React component is with findDOMNode. The docs indicated that the reason findDOMNode hadn't been removed was because there were no alternatives to use cases like this.

This seems like an extremely narrow edge case (why would you need a ref to a text node, right?), but @nytimes/react-prosemirror is heavily reliant on this API. Because this is a rich text editing library, we can't wrap text nodes with ref-able elements without introducing complexity/edge cases.

What should we do here? I understand the desire to remove a long-deprecated API, but React ProseMirror is now somewhat in a lurch.

A simplified version of how React ProseMirror uses findDOMNode:

export class TextNodeView extends Component<Props> {
  private viewDescRef: null | TextViewDesc | CompositionViewDesc = null;
  private renderRef: null | JSX.Element = null;

  updateEffect() {
    const { view, decorations, siblingsRef, parentRef, getPos, node } =
      this.props;
    // There simply is no other way to ref a text node
    // eslint-disable-next-line react/no-find-dom-node
    const dom = findDOMNode(this);

    let textNode = dom;
    while (textNode.firstChild) {
      textNode = textNode.firstChild as Element | Text;
    }

    // We construct a view descriptor tree to integrate with ProseMirror.
    // This is essentially ProseMirror's virtual DOM implementation. It
    // needs to contain references to each node that it's responsible for,
    // just like the React virtual DOM.
    if (!this.viewDescRef) {
      this.viewDescRef = new TextViewDesc(
        undefined,
        [],
        () => getPos.current(),
        node,
        decorations,
        DecorationSet.empty,
        dom,
        textNode
      );
    } else {
      this.viewDescRef.parent = parentRef.current;
      this.viewDescRef.children = [];
      this.viewDescRef.node = node;
      this.viewDescRef.getPos = () => getPos.current();
      this.viewDescRef.outerDeco = decorations;
      this.viewDescRef.innerDeco = DecorationSet.empty;
      this.viewDescRef.dom = dom;
      // @ts-expect-error We have our own ViewDesc implementations
      this.viewDescRef.dom.pmViewDesc = this.viewDescRef;
      this.viewDescRef.nodeDOM = textNode;
    }

    if (!siblingsRef.current.includes(this.viewDescRef)) {
      siblingsRef.current.push(this.viewDescRef);
    }

    siblingsRef.current.sort(sortViewDescs);
  }

  shouldComponentUpdate(nextProps: Props): boolean {
    return !shallowEqual(this.props, nextProps);
  }

  componentDidMount(): void {
    this.updateEffect();
  }

  componentDidUpdate(): void {
    this.updateEffect();
  }

  componentWillUnmount(): void {
    const { siblingsRef } = this.props;
    if (!this.viewDescRef) return;
    if (siblingsRef.current.includes(this.viewDescRef)) {
      const index = siblingsRef.current.indexOf(this.viewDescRef);
      siblingsRef.current.splice(index, 1);
    }
  }

  render() {
    const { node, decorations } = this.props;

    // This may wrap the text in, e.g., a span,
    // but usually returns a string
    return decorations.reduce(
      wrapInDeco,
      node.text
    );
  }
}
@eps1lon
Copy link
Collaborator

eps1lon commented Dec 18, 2024

Prior discussions:
#28926
#14357

@smoores-dev
Copy link
Author

Thanks, @eps1lon. I'll close this in favor of #28926, and add my comment there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants