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

Switch "location" prop in v6 #7117

Closed
Motoxpro opened this issue Feb 1, 2020 · 45 comments
Closed

Switch "location" prop in v6 #7117

Motoxpro opened this issue Feb 1, 2020 · 45 comments

Comments

@Motoxpro
Copy link

Motoxpro commented Feb 1, 2020

Hey guys!

Amazing work on v6. Just spent the last few hours toying around and converting a few things. Made so many things so much easier!

Two things as I was messing around and looking through the code:

  1. The component's "to" prop seems different? Instead passing an object like this
    props={pathname: '/home', state: {cool: true}} <Link to={props}>Link</Link>
    I have to do this:
    props={to: '/home', state: {cool: true}} <Link {...props}>Link</Link>

  2. It seems like the "location" prop from the Switch component is gone with Routes. If so, how would I implement a pattern like this?
    https://reacttraining.com/react-router/web/example/modal-gallery

@ajsmth
Copy link

ajsmth commented Feb 23, 2020

this would also affect the animated transition example, i would think: https://reacttraining.com/react-router/web/example/animated-transitions

@tconroy
Copy link

tconroy commented Apr 6, 2020

Yeah, I'm having trouble getting animations to work per the animated-transitions example. I think it's because the location prop no longer exists on Routes. Curious if anyone has found a solution for this yet.

@smkamranqadri
Copy link
Contributor

@Motoxpro here is answers to your questions.

  1. You have to pass state same as to like <Link to="/about" state={{ test: 'test }} />
  2. You have to use hook for location which is useLocation.

Here is the sample code.

https://github.com/smkamranqadri/react-router-v6-test

@smkamranqadri
Copy link
Contributor

Here is the live demo, click on the about link

https://react-router-v6-test.netlify.app

@Motoxpro
Copy link
Author

Thanks @smkamranqadri

For question 2, when you say

You have to use hook for location which is useLocation.

The example, https://reacttraining.com/react-router/web/example/modal-gallery , already uses useLocation. The problem seems to be that you need to show two routes at the same time if the "location" prop is a certain location but we no longer have a "location" prop

Could you maybe elaborate a bit more on how to achieve this?

@smkamranqadri
Copy link
Contributor

I'll look into that example later today.

@ajsmth
Copy link

ajsmth commented Apr 23, 2020

If you listen to the Full Stack Radio episode with @mjackson he suggests that there might be more advanced routing options coming. I can't quite recall exactly what he said but it I think it made sense for this use case

@MeiKatz
Copy link
Contributor

MeiKatz commented Apr 24, 2020

@ajsmth I tried to find the episode you mentioned but couldn't find it. Do you have a link to the episode?

@ajsmth
Copy link

ajsmth commented Apr 24, 2020

sorry i think it was actually React podcast! Episode 85

@MeiKatz
Copy link
Contributor

MeiKatz commented Apr 24, 2020

@ajsmth Perfect! I found it: https://reactpodcast.simplecast.fm/85

@strass
Copy link

strass commented Apr 27, 2020

I am also wondering about what to do here. I have the same need for the Switch.location prop but different use case (not animations but PIP browsing based on location state similar to the modal gallery example).

I got a proof of concept working usingmatchRoutes and a 'virtual path', although I'm sure there are many possible improvements here's what I worked out last Friday:

const Sidecar: FunctionComponent = () => {
  const location = useLocation();
  const virtualPath = location?.state?.sidecar ?? "/";
  const R = matchRoutes(
    [
      { path: "/2", element: <Test2 /> },
      { path: "/*", element: <Test1 /> },
    ],
    {
      pathname: virtualPath,
    }
  );
  const ElOne = R && R[0] ? R[0] : null;
  const Element = ElOne?.route.element;

  return <aside>{Element}</aside>;
};

@stale
Copy link

stale bot commented Jun 26, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
You can add the fresh label to prevent me from taking any action.

@stale stale bot added the stale label Jun 26, 2020
@ashkan-pm
Copy link

Any solutions or alternatives for this? I also can't get animations to work.

@stale stale bot removed the stale label Jun 27, 2020
@ashkan-pm
Copy link

I guess #7298 will fix the problem with animated route transitions.

@stale
Copy link

stale bot commented Aug 27, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
You can add the fresh label to prevent me from taking any action.

@stale stale bot added the stale label Aug 27, 2020
@timdorr timdorr added fresh and removed stale labels Aug 27, 2020
@lwyj123
Copy link

lwyj123 commented Dec 24, 2020

Same issues here, I was trying to add a page transition for my webapp, seems that the old example like this doesn't works for react-router v6. What's the solution in v6?

@MeiKatz

This comment has been minimized.

@MatejBransky
Copy link

MatejBransky commented Mar 19, 2021

In the meantime you can try the "fork" (kind of I couldn't run scripts in the original repo so I've created custom boilerplate) of react-router v6 (now I can continue on project where I've already started to use v6).

Demo - modal gallery: https://utilize-react-router.vercel.app/ (original: https://reactrouter.com/web/example/modal-gallery)

Packages:

It's a temporary workaround until Ryan and Michael have more time on public development of this package. (Then I deprecate these libs.)

@C-E-Rios
Copy link

Any updates on when this will be resolved?

@mjackson
Copy link
Member

Fixed in #7937

@wojtekmaj
Copy link

wojtekmaj commented Aug 18, 2021

That would be way too much pain. So I installed @MatejBransky's temporary fork:

yarn add @utilize/react-router @utilize/react-router-dom

and in order not to change all imports I added the following in Webpack config:

  resolve: {
    alias: {
+      // See https://github.com/ReactTraining/react-router/issues/7117
+      'react-router': '@utilize/react-router',
+      'react-router-dom': '@utilize/react-router-dom',
    },
  },

and I'm still hoping for the best. Best being location prop available officially again.

@arabold
Copy link

arabold commented Aug 18, 2021

@chaserda , if you only need modals, you can define subroutes. That works very well for me.

<Routes>
  <Route path="/assets" element={<ListAssetsPage />}>
    {/* Modals are rendered "on top" of the parent route */}
    <Route path="create" element={<CreateAssetDialog />} />
    <Route path="edit/:id" element={<EditAssetDialog />} />
  </Route>
</Routes>

Your parent page (in this example ListAssetsPage) also needs to define an <Outlet /> into which the subroutes are going to be rendered.

@ghost
Copy link

ghost commented Aug 18, 2021

@chaserda , if you only need modals, you can define subroutes. That works very well for me.

<Routes>
  <Route path="/assets" element={<ListAssetsPage />}>
    {/* Modals are rendered "on top" of the parent route */}
    <Route path="create" element={<CreateAssetDialog />} />
    <Route path="edit/:id" element={<EditAssetDialog />} />
  </Route>
</Routes>

Your parent page (in this example ListAssetsPage) also needs to define an <Outlet /> into which the subroutes are going to be rendered.

I thought I tried this and it didnt work unfortunately, the background was still blank. Maybe I will try again.

@mjackson
Copy link
Member

The main use case I can see for a <Routes location> prop is a global modal that is accessible from any route on the page, like a login screen. Let's reopen this until we can get a location prop into v6.

@mjackson mjackson reopened this Aug 31, 2021
@timdorr
Copy link
Member

timdorr commented Aug 31, 2021

I think what a lot of folks are describing here is a "stack" navigation that's popular in the React Native world. It's what #7943 is describing as well.

Note that this is distinct from just nesting routes. We can always do something like /foo/123/bar/321/baz/456?overlay. This is more for retaining bits of UI based on previous values on the history stack.

Maybe there's some sort of specific solution for that kind of use case?

@chaance
Copy link
Collaborator

chaance commented Sep 2, 2021

Added location to Routes and useRoutes (again!) for parity with Switch in #7989

@chaance chaance closed this as completed Sep 2, 2021
@gnapse
Copy link

gnapse commented Sep 2, 2021

Thank you!

@yyynnn
Copy link

yyynnn commented Oct 21, 2021

good usage example RRv6+React-spring

https://react-spring.io/hooks/use-transition#transitioning-between-routes

@mjackson
Copy link
Member

good usage example RRv6+React-spring

We are going to have an animation API in v6 that we will be recommending over <Routes location>. We do not recommend <Routes location> for doing animation in v6.

@erik-stephens
Copy link

We are going to have an animation API in v6 that we will be recommending over . We do not recommend for doing animation in v6.

Will this support different transition effects for different levels of routing? In my case, I use a fade transition for top level routes and then a slide transition for drill-down/detail panel routes. I suspect that's a common pattern.

I'm able to do this in v5 by handling the switching/matching. In v6, I thought I'd be able to insert a different around the drill-down child routes, but I bump into the "only elements are allowed" invariant.

If someone has an example of how to accomplish this, please share. Thank you!

@mwood23
Copy link

mwood23 commented Nov 23, 2021

I know it's not recommended, but it's needed for an app I'm building and we're already to far down the V6 rabbit hole to turn back now. Here's a code snippet of something working for me!

Sandbox: https://codesandbox.io/s/react-router-v6-animated-routes-9zm9p

Note: Be careful of putting any context inside of the Wrapper component. Since you are using the key prop it gets blown away on route changes.

import { CSSProperties, FC } from "react";
import { Outlet, Route, Routes, useLocation } from "react-router-dom";
import { AnimatePresence, motion } from "framer-motion";
import { BrowserRouter, Link } from "react-router-dom";

const styles: CSSProperties = {
  minHeight: "100vh",
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
  fontSize: "40px"
};

const Route1 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "burlywood" }}>
      <p>🥝</p>
      <Link to="/route-2">Route 2</Link>
      <Link to="/route-3">Route 3</Link>
    </div>
  );
};

const Route2 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "cornflowerblue" }}>
      <p>🍎</p>
      <Link to="/route-2">Route 2</Link>
      <Link to="/route-3">Route 3</Link>
    </div>
  );
};

const Route3 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "papayawhip" }}>
      <p>🍑</p>
      <Link to="/">Route 1</Link>
      <Link to="/route-2">Route 2</Link>
    </div>
  );
};

export const Wrapper: FC = () => {
  return (
    <motion.div
      initial="initial"
      animate="in"
      exit="out"
      variants={{
        initial: {
          opacity: 0
        },
        in: {
          opacity: 1
        },
        out: {
          opacity: 0
        }
      }}
      transition={{
        type: "spring",
        damping: 10,
        stiffness: 50
      }}
    >
      <Outlet />
    </motion.div>
  );
};

export const AnimatedRoutes: FC = () => {
  const location = useLocation();

  return (
    <AnimatePresence exitBeforeEnter>
      <Routes location={location} key={location.pathname}>
        <Route element={<Wrapper />}>
          <Route element={<Route1 />} path="/" />
          <Route element={<Route2 />} path="/route-2" />
          <Route element={<Route3 />} path="/route-3" />
        </Route>
      </Routes>
    </AnimatePresence>
  );
};

export default function App() {
  return (
    <BrowserRouter>
      <AnimatedRoutes />
    </BrowserRouter>
  );
}

@chinaxcy
Copy link

chinaxcy commented Dec 5, 2021

I know it's not recommended, but it's needed for an app I'm building and we're already to far down the V6 rabbit hole to turn back now. Here's a code snippet of something working for me!

Sandbox: https://codesandbox.io/s/react-router-v6-animated-routes-9zm9p

Note: Be careful of putting any context inside of the Wrapper component. Since you are using the key prop it gets blown away on route changes.

import { CSSProperties, FC } from "react";
import { Outlet, Route, Routes, useLocation } from "react-router-dom";
import { AnimatePresence, motion } from "framer-motion";
import { BrowserRouter, Link } from "react-router-dom";

const styles: CSSProperties = {
  minHeight: "100vh",
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
  fontSize: "40px"
};

const Route1 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "burlywood" }}>
      <p>🥝</p>
      <Link to="/route-2">Route 2</Link>
      <Link to="/route-3">Route 3</Link>
    </div>
  );
};

const Route2 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "cornflowerblue" }}>
      <p>🍎</p>
      <Link to="/route-2">Route 2</Link>
      <Link to="/route-3">Route 3</Link>
    </div>
  );
};

const Route3 = () => {
  return (
    <div style={{ ...styles, backgroundColor: "papayawhip" }}>
      <p>🍑</p>
      <Link to="/">Route 1</Link>
      <Link to="/route-2">Route 2</Link>
    </div>
  );
};

export const Wrapper: FC = () => {
  return (
    <motion.div
      initial="initial"
      animate="in"
      exit="out"
      variants={{
        initial: {
          opacity: 0
        },
        in: {
          opacity: 1
        },
        out: {
          opacity: 0
        }
      }}
      transition={{
        type: "spring",
        damping: 10,
        stiffness: 50
      }}
    >
      <Outlet />
    </motion.div>
  );
};

export const AnimatedRoutes: FC = () => {
  const location = useLocation();

  return (
    <AnimatePresence exitBeforeEnter>
      <Routes location={location} key={location.pathname}>
        <Route element={<Wrapper />}>
          <Route element={<Route1 />} path="/" />
          <Route element={<Route2 />} path="/route-2" />
          <Route element={<Route3 />} path="/route-3" />
        </Route>
      </Routes>
    </AnimatePresence>
  );
};

export default function App() {
  return (
    <BrowserRouter>
      <AnimatedRoutes />
    </BrowserRouter>
  );
}

This causes components inside the Wrapper to be rendered repeatedly

@remix-run remix-run deleted a comment from chinaxcy Dec 6, 2021
@mleister97
Copy link

mleister97 commented Jan 5, 2022

Please see an example on how to do animations in RRv6 & react-transition 4 here: https://codesandbox.io/s/lqc45

@khayrullaev
Copy link

khayrullaev commented Jan 7, 2022

@mleister97 I saw your implementation. I was curious, is it possible to apply animations only to certain pages(to imitate react-navigation's stack navigator animation) not all?

@mleister97
Copy link

@khayrullaev I am not sure about a performant solution. Maybe you can watch for the location hook to change and adapt the className of CSSTransition accordingly. Probably there are better solutions out there. Please let me know if you figure out how to do that.

@badasukerubin
Copy link

We are going to have an animation API in v6 that we will be recommending over <Routes location>. We do not recommend <Routes location> for doing animation in v6.

Is there an update regarding this? @mjackson

@laurencefass
Copy link

laurencefass commented Jan 8, 2022

Why would a Router intrinsically support Animations? Isnt that breaking the single-responsibility principle?

@Kamikozz
Copy link

Kamikozz commented Mar 19, 2022

@mleister97 but what about redirect?
if we just add <Navigate to="/first" replace />

<Routes location={location}>
  <Route path="/first" element={<Page1 />} exact />
  <Route path="/second" element={<Page2 />} />
  <Route path="/third" element={<Page3 />} />
  <Route path="/swag" element={<Navigate to="/first" replace />} />
</Routes>

then it will cause multiple re-renders on redirect and blow-up the page
image

@zhouzi
Copy link

zhouzi commented Jun 2, 2022

As pointed in #8470, the implementation seems incomplete in regard to https://reacttraining.com/react-router/web/example/modal-gallery

Although the <Routes /> component now has a location prop, it's not passed down through the location context. So the <Routes />'s children are not reading from it.

Here's a reproduction: https://codesandbox.io/s/vigilant-alex-j58zg6?file=/src/App.js

@lovelidevs
Copy link

good usage example RRv6+React-spring

We are going to have an animation API in v6 that we will be recommending over <Routes location>. We do not recommend <Routes location> for doing animation in v6.

Any updates on getting animated navigations into v6? Thank you!

@flying-sheep
Copy link

flying-sheep commented Dec 20, 2022

@lovelidevs It works now! See https://github.com/nanxiaobei/react-slide-routes

@zhouzi updating react-router-dom to >=6.5.0 in your example also makes everything work as expected: https://codesandbox.io/s/happy-farrell-l1ce5h?file=/package.json

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

No branches or pull requests