reactjs / react-transition-group

An easy way to perform animations when a React component enters or leaves the DOM
https://reactcommunity.org/react-transition-group/
Other
10.15k stars 650 forks source link

Cannot get CSSTransition and Route to work #763

Open cannon303 opened 3 years ago

cannon303 commented 3 years ago

Hi I am hoping for a bit of help here if possible. I cannot get transition group to work. The routing works fine but there are no transitions and the console.log("FIRED") isn't outputted either. I believe the Switch component isn't supported here and that maybe what is causing the problem? However I cannot find the right documentation to tell me what to change it to. Here is a simple example where specific components fade in and out at the same time when a link is clicked. I also get an error in strict mode in the console which I'll post at the bottom. Can anyone help me please?

import * as React from 'react'
import {
    HashRouter as Router,
    Link,
    Switch,
    Route,
    Redirect,
    useParams,
    useLocation
} from "react-router-dom";
import {TransitionGroup, CSSTransition} from "react-transition-group";

const TransGroup = () =>{

    const location = useLocation();

    return(
        <>
            <div className={"w-full h-full"}>
        <Router>
            <div className={"fill w-full"}>

                <ul className={"nav overflow-hidden mb-8 flex "}>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/"}>Red</Link></li>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/purple"}>Purple</Link></li>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/blue"}>Blue</Link></li>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/yellow"}>Yellow</Link></li>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/pink"}>Pink</Link></li>
                    <li><Link className={"bg-white p-8 m-8 text-black inline-block"} to={"/aqua"}>Aqua</Link></li>

                </ul>
            </div>
        </Router>
        <div className={"fill content w-full h-full"}>
            <Route render={({location}) =>(
            <TransitionGroup>
                <CSSTransition
                    timeout={5000}
                    onEnter={() => {
                        console.log('FIRED!');
                    }}
                    classNames={{
                        appear: 'my-node-appear',
                        appearActive: 'my-node-active-appear',
                        appearDone: 'my-node-done-appear',
                        enter: 'my-node-enter',
                        enterActive: 'my-node-active-enter',
                        enterDone: 'my-node-done-enter',
                        exit: 'my-node-exit',
                        exitActive: 'my-node-active-exit',
                        exitDone: 'my-node-done-exit',
                    }}
                    key={location.key}

                >
            <Switch>
                <Route exact path={"/"}>

                    <div className={"bg-red-500 text-white w-full h-16"}>
                        RED
                    </div>
                </Route>
                <Route path={"/purple"}>

                    <div className={"bg-lcnuk-other text-white w-full h-16"}>
                        PURPLE
                    </div>
                </Route>
                <Route path={"/blue"}>

                    <div className={"bg-blue-500 text-white w-full h-16"}>
                        BLUE
                    </div>
                </Route>
                <Route path={"/yellow"}>

                    <div className={"bg-lcnuk-other text-white w-full h-16"}>
                        YELLOW
                    </div>
                </Route>
                <Route path={"/pink"}>

                    <div className={"bg-white text-black w-full h-16"}>
                        PINK
                    </div>
                </Route>
                <Route path={"/aqua"}>

                    <div className={"bg-black text-white w-full h-16"}>
                        AQUA
                    </div>
                </Route>
                <Route path={'*'}>
                    <div>Not Found</div>
                </Route>
            </Switch>
                </CSSTransition>
            </TransitionGroup>
            )} />
        </div>
            </div>
        </>
    )
}
export default TransGroup

**I'm a bit stuck as React is something I'm new to and looking at other examples I cannot see how they relate to my project as they look so different. Here is the error in console:

Warning: Legacy context API has been detected within a strict-mode tree.

The old API will be supported in all 16.x releases, but applications using it should migrate to the new version.

Please update the following components: Transition, TransitionGroup

Learn more about this warning here: https://reactjs.org/link/legacy-context TransitionGroup@http://localhost:3000/static/js/vendors~main.chunk.js:328926:30 Route@http://localhost:3000/static/js/vendors~main.chunk.js:324374:29 div div TransGroup@http://localhost:3000/static/js/main.chunk.js:1450:88 Router@http://localhost:3000/static/js/vendors~main.chunk.js:324005:30 HashRouter@http://localhost:3000/static/js/vendors~main.chunk.js:323573:35 index.js:1**

​Anyone know where I'm going wrong please?

jordanarldt commented 2 years ago

@cannon303 Here's a codesandbox example that should be helpful!

Essentially, each Route needs it's own CSSTransition, and each CSSTransition needs a nodeRef for the individual route.

////// CONTENT TRANSITION ROUTER
const PageContent = withRouter(({ location }) => {
  let routeRefs: any[] = [];

  const isMatch = useCallback(
    (path: string): boolean => {
      return location.pathname === path ?? false;
    },
    [location]
  );

  return (
    <>
      {appRoutes.map(({ path, Component }, index) => {
        routeRefs[index] = React.useRef(null);

        return (
          <Route key={index} exact path={path}>
            {() => {
              // Route callback ensures the transitions are loaded correctly
              return (
                <CSSTransition
                  nodeRef={routeRefs[index]}
                  in={isMatch(path)}
                  timeout={300}
                  classNames="fade"
                  unmountOnExit
                  appear
                >
                  <div ref={routeRefs[index]} className="fade">
                    <Component />
                  </div>
                </CSSTransition>
              );
            }}
          </Route>
        );
      })}
    </>
  );
});
doanhtu07 commented 2 years ago

I am using react-transition-group v4.4.1.

Here is what I did and worked.

const { location } = this.props; // use with withRouter
const transitionKey = location.pathname;

<div className={classes.root}>
  <TransitionGroup component={null}>
    <CSSTransition
      key={transitionKey}
      classNames="slideLeftToRight"
      addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
    >
      <Switch location={location}>

        <Route path="/">
          <div id="page 1" />
        </Route>

        <Route path="/another">
          <div id="page 2" />
        </Route>

      </Switch>
    </CSSTransition>
  </TransitionGroup>
</div>

TransitionGroup looks at the transition key, and if there is a change in pathname, TransitionGroup will execute the transitioning between components.

epletcher72 commented 2 years ago

Hey Jordan! I love your SandBox. I have a very specific use case that needs HashRouter, and I am using v6. I am trying to play with your code and get ur demo working in my case, but I am having difficulty getting the out transition to work. Is there any reason you can see this adaptation wouldn't work?

function StackNavigator() {
  const location = useLocation();
  const routeRefs = useRef<any>([]);
  const currentKey = location.pathname.split("/")[1] || "/";

  const isMatch = useCallback(
    (path: string): boolean => {
      return location.pathname === path ?? false;
    },
    [location],
  );

  return (
    <Routes>
      {tabRoutes.map(({ route, Element }, index) => {
        return (
          <Route
            key={route}
            path={route}
            element={
              <CSSTransition
                key={currentKey}
                nodeRef={routeRefs.current[index]}
                in={isMatch(route)}
                timeout={300}
                classNames="fade"
                unmountOnExit
                appear
              >
                <div
                  ref={(ref) => (routeRefs.current[index] = ref)}
                  style={{ height: "100%", width: "100%" }}
                  className="fade"
                >
                  <Element />
                </div>
              </CSSTransition>
            }
          ></Route>
        );
      })}
    </Routes>
  );
}

It does seem as though the refs aren't having an effect, But I don't know why that would be.