3
votes

Hi all Im just trying to get my head around using Refs to target elements in my components during router transitions. currently The onEnter, onExit and addEndListener events from React Transition Group already give you access to the DOM node you're animating. Which was fine for a while but now as you may already know you get the error.

index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Transition which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely [1]

here is my code for App.js

    import React from 'react';
    import { BrowserRouter as Router, Route } from 'react-router-dom';
    import { CSSTransition } from 'react-transition-group';
    import { gsap } from 'gsap';
  

    import Header from './tools/header';
    import About from './pages/about';
    import Home from './pages/home';

    const routes = [
        { path: '/', name: 'Home', Component: Home },
        { path: '/about', name: 'About', Component: About },

    ]

    function Routeapp() {

        const nodeRef = React.useRef(null);

        const onEnter = (node) => {
            console.log(node)

            gsap.from(
                [node.children[0].firstElementChild, node.children[0].lastElementChild],
                {
                    duration: 0.6,
                    y: 30,
                    delay: 0.6,
                    ease: 'power3.inOut',
                    opacity: 0,
                    stagger: {
                        amount: 0.3,
                    },
                }
            );

        };

        const onExit = node => {
            gsap.to(
                [node.children[0].firstElementChild, node.children[0].lastElementChild],
                {
                    duration: 0.6,
                    y: -30,
                    ease: 'power3.inOut',
                    opacity: 0,
                    stagger: {
                        amount: 0.15,
                    },
                }
            );
        };

      return (
        <Router>
          <Header />
          <div className="container">
              {routes.map(({ path, Component }) => (
                  <Route key={path} exact path={path}>
                      {({ match }) => (
                          <CSSTransition
                              in={match != null}
                              key={path}
                              timeout={1200}
                              classNames="page"
                              onEnter={onEnter}
                              onExit={onExit}
                              unmountOnExit={true}

                          >
                              <div className="page" ref={nodeRef} >
                                  <Component />
                              </div>
                          </CSSTransition>
                      )}
                  </Route>
              ))}
          </div>
        </Router>
      );
    }
    export default Routeapp;

Home.js

import React from 'react';
import Title from '../tools/title';

const Home = () => {
  return (

          <div className="inner">
              <Title lineContent="this is the" lineContent2="Home page" />
              <div className={'infoBox'}>
                  <p className="info">hello mel and welcome</p>
              </div>
          </div>
  );
};

export default Home;

about.js

import React from 'react';
import Title from '../tools/title';

const About = () => {
  return (

        <div className="inner">
          <Title lineContent={'this is the'} lineContent2={'About page'} />
          <div className={'infoBox'}>
            <p className="info pink">
              Lorem Ipsum is simply dummy text of the printing and typesetting
              industry. Lorem Ipsum has been the industry's standard dummy text ever
              since the 1500s, when an unknown printer took a galley of type and
              scrambled it to make a type specimen book. It has survived not only
              five centuries, but also the leap into electronic typesetting,
              remaining essentially unchanged. It was popularised in the 1960s with
              the release of Letraset sheets containing Lorem Ipsum passages, and
              more recently with desktop publishing software like Aldus PageMaker
              including versions of Lorem Ipsum.
            </p>
          </div>
        </div>
  );
};

export default About;

My issue is that I am trying to find a way to 'Ref' my 'Home' and 'About' components so that I can not only animate them but possible add different tweens to the 'onExit' and 'onEnter props if possible.

I have tried to 'Ref' the components directly by trying this in app.js:

return (
    <Router>
      <Header />
      <div className="container">
          {routes.map(({ path, Component }) => (
              <Route key={path} exact path={path}>
                  {({ match }) => (
                      <CSSTransition
                          in={match != null}
                          key={path}
                          nodeRef={nodeRef}
                          timeout={1200}
                          classNames="page"
                          onEnter={onEnter}
                          onExit={onExit}
                          unmountOnExit={true}

                      >
                          <div className="page" ref={nodeRef} >
                              <Component />
                          </div>
                      </CSSTransition>
                  )}
              </Route>
          ))}
      </div>
    </Router>
  );

as expected this causes my application to stop working since 'nodeRef' returns false and When nodeRef prop is used, node is not passed to callback functions (e.g. onEnter) because user already has direct access to the node.

So I just need a new way of doing this now for future reference, any insights or ideas will be most welcomed.

thanks

3
I don't see findDOMNode anywhere in the code you included. Is it being called by a third party library you're using? If so, there's nothing you can reasonably do.Slbox
@Sibox it is being used by React-Transition-Group to refer to the node. reactcommunity.org/react-transition-group/transitionW9914420
If it's being invoked by React Transition Group there's nothing you can do. I'm not too familiar with the underlying mechanics of that package, but here's a discussion from their issue tracker.Slbox
@Slbox many thanks for you help. It made me find an answer please see below :)W9914420

3 Answers

2
votes

So I just want to give a shout out to @Slbox who inspired me to dig deeper and relook at the problem.

Disclaimer

I am not an expert or professional Programmer in javascript and I by no means suggest that this is the only way of doing this, my presentation should be taken in an abstract fashion. My thoughts only explain the theoretical premises for which the solution is based on and implementation of this will depend upon your own architecture.

Finding the node 'findDOMNode'

So in React Strict mode 'FindDomNode is being depreciated and a 'Ref' friendly approached is now being favoured when you are wanting to refer to an element on your page. Like so many I was getting that pesky error message that pops up an tells you that 'findDOMNode has been depreciated in Strict Mode'.

Use Case (why where you having problems)

In React Router I had created a simple page transition using the combination of 'CSSTransition from 'react-transition-group and the Javascript based animation platform 'GSAP'. the issue that I was having was I having used a DOM node approach in referencing elements to the preferred way in react.

1. Restructure the app component

// So the App now has the routes contained inside each component in order for it
// to work and add additional functions and flexibility.

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';

import './route.scss';

import Header from './tools/header';
import About from './pages/about';
import Home from './pages/home';

function Routeapp() {


  return (
    <Router>
      <Header />
      <div className="container">
          <Home /> <!-- routes and funcs -->
          <About /> <!-- routes and funcs -->
      </div>
    </Router>
  );
}

export default Routeapp;

2. The home / about components

import React, {useRef} from 'react';
import Title from './title';
import { CSSTransition } from 'react-transition-group';
import {  Route} from 'react-router-dom';
import {gsap} from "gsap";


const Home = () => {

    // These two Refs are needed to refer to elements that we will be animating
    const title = useRef(null)
    const info = useRef(null)

    // This Ref will be used to make reference to the page of each component. 
    // My understanding is that this Ref will be used to distinguish between each 
    // component that is using 'CSSTransition' component and also removes the 
    // 'findDOMNode' warning
    const nodeRef = useRef(null)

    // This function is called immediately after the 'enter' 
    // or 'appear' class is applied from CSSTransition component.

    const onEnter = () => {
        gsap.from(
            [title.current,info.current],
            {
                duration: 0.6,
                y: 30,
                delay: 0.6,
                ease: 'power3.inOut',
                opacity: 0,
                stagger: {
                    amount: 0.3,
                },
            }
        );
    };


    // This function is called immediately after the 'exit' 
    // class is applied from CSSTransition component.
    const onExit = () => {
        gsap.to(
            [title.current,info.current],
            {
                duration: 0.6,
                y: -30,

                ease: 'power3.inOut',
                opacity: 0,
                stagger: {
                    amount: 0.15,
                },
            }
        );

    };


  return (

    <Route
        exact path={'/'}
        children={({ match }) => (
            <CSSTransition
                in={match != null}
                // time should not really be longer than the time it takes the animation
                // to leave the scene.
                timeout={1200}
                classNames={'page'}
                nodeRef={nodeRef}
                onEnter={onEnter}
                onExit={onExit}
                unmountOnExit
            >
                <div className={'page'} ref={nodeRef}>
                    <div className="inner">
                        <Title forwardRef={title} lineContent="this is the" lineContent2="Home page" />
                        <div ref={info} className={'infoBox'}>
                            <p className="info">hello stackOverflow and welcome</p>
                        </div>
                    </div>
                </div>
            </CSSTransition>
        )}
    />

  );
};


export default Home;

GSAP and React-Router EXAMPLE

Conclusion

This is my method of achieving this effect, but if there are better or alternative ways then please add to the discussion and share.

1
votes

The answer by @W9914420 guided me to find a workaround to this issue.

I would just add that from all the lines of code, the most relevant is:

  • Declare an instance to a reference:
    const nodeRef = useRef(null)
  • Inject that reference as a property into the component:
    <CSSTransition ``
        in={match != null}
        // time should not really be longer than the time it takes the animation
        // to leave the scene.
        timeout={1200}
        classNames={'page'}
        nodeRef={nodeRef}
        onEnter={onEnter}
        onExit={onExit}
        unmountOnExit
        >

Thanks @W9914420 for the tip!

1
votes

the quick solution is :

./src/index.js file and change it from this:

   ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

to this:

ReactDOM.render(
    <App />
  document.getElementById('root')
);