CSSTransition nodeRef for component with direct children
Asked Answered
A

1

1

I want to get rid of the warning on StrictMode for findDOMNode when using react-transition-group but I stumbled upon an issue.

My <Slide> component looks like this:

class Slide extends React.Component {
  nodeRef = React.createRef();

  render() {
    return (
      <CSSTransition
        in={this.props.in}
        timeout={ANIMATION_DURATION}
        mountOnEnter={true}
        unmountOnExit={true}
        classNames={{
          enter: "slideEnter",
          enterActive: "slideEnterActive",
          exit: "slideExit",
          exitActive: "slideExitActive"
        }}
        nodeRef={this.nodeRef}
      >
        {this.props.children}
      </CSSTransition>
    );
  }
}

It receives a Drawer element as children, the Drawer component looks like this:

class Drawer extends React.Component {
  render() {
    return (
      <div className="drawer">
        <button onClick={this.props.onClose}>close me</button>{" "}
        <div>This is my drawer</div>
      </div>
    );
  }
}

I cannot wrap the children element with a HTML tag (to attach a ref <div ref={this.nodeRef}>{this.props.children}</div> because it breaks the animation of the content. (I'm using this for children that are different drawers with position absolute)

I've also tried with cloneElement but it still doesn't work (with the code from below it behaves like this: 1. in no animation, 2. out no animation, 3. in animation works but I get the warning findDOMNode so it seems that nodeRef is sent as null, 4. out animation does not work.

const onlyChild = React.Children.only(this.props.children);
const childWithRef = React.cloneElement(onlyChild, {
  ref: this.nodeRef;
});

Is there any solution for this situation? Thanks!

Ahq answered 29/7, 2020 at 8:51 Comment(8)
Could you try setting ref: this.nodeRef instead? If that fixes it, I'll write an answer explaining it.Hepsibah
If I use it like that I get an error Uncaught TypeError: Cannot read property 'baseVal' of undefined at hasClass (hasClass.js?1d84:3) at addClass (addClass.js?0d03:3) at eval (CSSTransition.js?e9c9:13) at Array.forEach (<anonymous>) at addClass (CSSTransition.js?e9c9:12) at CSSTransition.addClass (CSSTransition.js?e9c9:237) at Object.CSSTransition._this.onEnter (CSSTransition.js?e9c9:117) at Transition.performEnter (Transition.js?9b73:262) at Transition.updateStatus (Transition.js?9b73:228) at Transition.componentDidUpdateAhq
I was able to do it in my example. Could you reproduce your problem in a CodeSandbox? Just a small version of the situation you're in.Hepsibah
the error appears when you click the button codesandbox.io/s/bold-fast-jvg83?file=/src/Slide.tsxAhq
I applied some edits to your question to include relevant info (it's in review). I also suggest that you edit your question to say ref: this.nodeRef, just so my answer doesn't have to address two problems, only the core one.Hepsibah
Hey, not sure if you noticed, but I posted an answer a while ago which should solve the issue.Hepsibah
Hey. sorry, I was under the impression that I've sent a reply to you. Yep. The solution makes sense. Thanks! I just have to see how I can adapt it to my specific cases as I have a couple of intermediate react components in between. It's smth like <Slide><DrawerCycle></Slide> then DrawerCycle can render multiple types of drawers and inside each type I use the actual <Drawer> comp. So I have to propagate that ref prop through all the components until it gets to the one that uses a DOM element, but I don't like it that much :(Ahq
Yeah, sounds problematic, I hope you find an easier way than ref drilling 😬 Let me know on Twitter if you need some more help.Hepsibah
H
3

The problem is that nodeRef needs to point to a DOM Node, as the name suggests, in your case it points to an instance of the Drawer class. You have two options:

  1. Pass the ref through another prop, e.g. forwardedRef, and in the Drawer class pass that prop to the root element:
React.cloneElement(onlyChild, {
  forwardedRef: this.nodeRef,
})
<div ref={this.props.forwardedRef} className="drawer">
  1. Convert Drawer to a function component and use React.forwardRef:
const Drawer = React.forwardRef((props, ref) => {
  return (
    <div ref={ref} className="drawer">
      <button onClick={props.onClose}>close me</button>{" "}
      <div>This is my drawer</div>
    </div>
  );
});
Hepsibah answered 1/8, 2020 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.