I'm struggling to make React-Router work well with CSSTransition, which seems to be the defacto library for this around the web.
I'm using React-Router v6 without any issue. I'm not sure if the issue is due to that or not being that it is more recent.
Goal
My goal is to animate between my different route with an animation that plays when clicked-in and another that plays when clicked-out.
In term of style, I'm just looking at a simple "Slide-in" animation. Where my first component will slide-in-from-bottom and slide-out-to-bottom before the second component will slide-in-from-right and eventually slide-out-to-right when clicked out. and things like that.
Issues
I can't believe this is so hard to put into practice. I am definitely not the first one that wants to do this.
I'm open to any suggestion that'll work and that uses React-Router.
I have tried to use CSStransition in every way possible so far.
I tried without it and tried making a new functional component that takes in "Location" and "PreviousLocation"(Custom State) in combination with useEffect to animate the div around the route before it is rendered but was way over my head and couldn't make it happen. Not sure how to prevent rendering while keeping what's already rendered and wait for the animation to finish to allow the render.
I would gladly take any help and any method that works at this point.
thanks.
My bare code just for reference (I have discarded everything I've tried and started fresh):
const App = () => {
const location = useLocation();
return (
<div className="app">
<section className="main">
<Routes>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="marketplace" element={<Marketplace />} >
<Route index element={<Listing />} />
<Route path="listing" element={<Listing />} />
<Route path="cart" element={<Cart />} />
</Route>
</Routes>
</section>
<nav className="main-nav">
<ul>
<li>
<NavLink to="/home" className={(navdata) => (navdata.isActive ? 'active' : 'none')} >
<span className="nav-text">Home</span>
</NavLink>
</li>
<li>
<NavLink to="/marketplace" className={(navdata) => (navdata.isActive ? 'active' : 'none')} >
<span className="nav-text">Store</span>
</NavLink>
</li>
<li>
<NavLink to="/aboutus" className={(navdata) => (navdata.isActive ? 'active' : 'none')} >
<span className="nav-text">About Us</span>
</NavLink>
</li>
<li>
<NavLink to="/user" className={(navdata) => (navdata.isActive ? 'active' : 'none')} >
<span className="nav-text">My Account</span>
</NavLink>
</li>
</ul>
</nav>
</div>
);
};
The key part of a solution to creating transitions between routes is dealing with exit transitions, and making sure the old route doesn't disappear instantly so we have time to see the exit. By passing a function as the child of Route, we are able to achieve that.
To achieve a unique transition on each Route we simply need to provide each Route with it's own CSSTransition and set up the css properties for each transition.
A single Route would look like this:
<Route path="/user">
{({ match }) => (
<CSSTransition
in={match != null}
classNames="spin"
timeout={500}
unmountOnExit
>
<div className="absolute">
<Marketplace />
</div>
</CSSTransition>
)}
</Route>
And here is a full example set up in codesandbox with four example of routes with unique transitions. They are not very pleasing animations, but are designed to show the concept clearly.
This page has some great info about how to do this and what potential issues to watch out for, such as the need to use absolute or fixed positioning:
Exit transitions will cause the content of routes to linger until they disappear, which might pose some styling challenges. Make sure that routes don't affect each other's layout, for example you can remove them from the flow of the document by using absolute or fixed positioning.
Related
Hi am trying to update project using react router to v6. I get the basics but am struggling with relative links.
We have a page that renders reference documentation for a given item by 'id'. The documentation can have links to other 'sibling' material, using sibling id. In other words the user can navigate around the docs without ever leaving the same basic route (parameterised by 'id').
I have made a little repro on codesandbox, with essential code below.
import React from "react";
import {
BrowserRouter,
Routes,
Route,
Link,
useParams
} from "react-router-dom";
import "./styles.css";
function GenericPage() {
const { id } = useParams();
return (
<div className="page">
<p>Document id: {id}</p>
<div>
Links from within page don't work:
<Link to="./foo" className="link">
Foo
</Link>
<Link to="./bar" className="link">
Bar
</Link>
</div>
</div>
);
}
export default function App() {
return (
<BrowserRouter>
<div>
Working top nav:
<Link to="docs/generic/foo" className="link">
Foo
</Link>
<Link to="docs/generic/bar" className="link">
Bar
</Link>
</div>
<Routes>
<Route path="/docs/generic/:id" element={<GenericPage />} />
</Routes>
</BrowserRouter>
);
}
Have tried using ../{id}, etc to no avail.
Maybe this is by design but it seems a bit odd to disallow a link to sibling pages using a simple relative name. All pretty normal in regular web dev.
I'm a bit surprised I hadn't run across this one yet, but it seems to be by design. There's a discussion here that you may find illuminating, or frustrating. There does appear to be some workarounds though.
Apparently the ".." means to "go up one Route". When there's only one Route the code is defaulting to the root "/" "route" that the app is being rendered on.
You can restructure your routes to match the path structure.
Example:
<Routes>
<Route path="/docs">
<Route path="generic">
<Route path=":id" element={<GenericPage />} />
</Route>
</Route>
</Routes>
Now the relative links appear to work as you and I, and I suspect many others, expect them to.
function GenericPage() {
const { id } = useParams();
return (
<div className="page">
<p>Document id: {id}</p>
<div>
Links from within page now work:
<Link to="../foo" className="link">
Foo
</Link>
<Link to="../bar" className="link">
Bar
</Link>
</div>
</div>
);
}
You can nest your parameterized route under the main one, like this:
<Routes>
<Route path="/docs/generic" element={<GenericPage />}>
<Route path=":id" element={<GenericPage />} />
</Route>
</Routes>
The nesting allows the nested path to get tacked onto the end of its parent, so the final route is /docs/generic/:id.
After that, update your in-page links so you only pass the parameter value to the to prop instead of the whole route:
<div>
<Link to="foo" className="link">
Foo
</Link>
<Link to="bar" className="link">
Bar
</Link>
</div>
This allows you to change the actual route value ("/docs/generic") in your router without having to update the links in your GenericPage component.
See it on Codesandbox.
All previous answers are correct and well illustrate the design of the react router v6. However, imho. the proposed solutions are workarounds so that, in this case, the links act as expected.
Much simpler however would be to use the relative="path" flag in the <Link /> component. See the documentation:
By default, links are relative to the route hierarchy, so .. will go up one Route level. Occasionally, you may find that you have matching URL patterns that do not make sense to be nested, and you're prefer to use relative path routing. You can opt into this behavior with relative
This has the big advantage to also function as expected if the component which is rendering the <Link /> component is not in the same hierarchie as the "last" route element, which will often be the case for Layout components.
In your case, simply replace
<Link to="./foo" className="link">
by
<Link to="./foo" relative="path" className="link">
I am currently using
"react-router": "^6.0.0-beta.0",
"react-router-dom": "^6.0.0-beta.0",
The problem with NavLink of react-router-dom that i am facing now is that the root path "/" is always active, other paths are of no problem and they are being active and inactive during toggle, its just the root path that is giving me trouble, i have searched and tried many solutions. but nothing worked.
Use "exact" and "exact={true}" both, but no success.
Used this:
<NavLink
className="iconContainer"
exact={true}
to="/"
activeClassName="isActive"
>
<span className="menu-title">Home</span>
</NavLink>
and Also this:
<NavLink
className="iconContainer"
exact
to="/"
activeClassName="isActive"
>
<span className="menu-title">Home</span>
</NavLink>
I have been stuck in this situation for past two days, and still no success in finding any solution.
Any help will be appreciated, thanks
Edit:
My routes
<Routes>
<Route
exact
path="order/:orderId"
element={<OrderDetails />}
></Route>
<Route
exact
path="orders"
element={<Orders />}
></Route>
<Route
exact
path="/"
element={<Home />}
></Route>
</Routes>
Exact param will no longer working on NavLink component. In version 6 Beta they have included a new param called: end
With this simply approach you just need to pass end param for your NavLink component and exact to you Route
<NavLink end to="/">
Go to Home
</NavLink>
When you write end="another-class-active" you can change your active className which is active by default.
As #Greg Wozniak mentioned end is a boolean so you can't change active class name with this, instead of this you can pass a function to className:
className={({ isActive }) =>
isActive ? 'activeClassName' : undefined
}
For more information read this:
https://reactrouter.com/docs/en/v6/api#navlink
Working example:
https://codesandbox.io/s/vigorous-thompson-e7k8eb
Note that this is still version Beta so we need to wait for some fixes and official releases
In 6.0.2 you can pass a function to className and that function gets a set of props. One of those props is "isActive". Here is how I solved it for the OP code:
<NavLink
className={(props) => {
return `${props.isActive ? 'isActive ' : ''}iconContainer`;
}}
end
to="/"
>
<span className="menu-title">Home</span>
</NavLink>
Also note that the class "active" is automatically set by the
NavLink.
Also note the use of end: "If the end prop is used, it will ensure this component isn't matched as "active" when its descendant paths are matched. For example, to render a link that is only active at the website root and not any other URLs"
However this how I did it, you don't have to use exact in the case of NavLink as said by the official docs, LINK TO THE DOCS
<NavLink to="/service" className='turner'>
Services
</NavLink>
I have a navbar with three components/links. The structure looks like this:
<ul className="flex text-white flex-col lg:flex-row list-none lg:ml-auto">
<NavLink
activeClassName="active"
to="/"
>
Home
</NavLink>
<NavLink
activeClassName="active"
to="/about"
>
<span className="ml-2">About</span>
</NavLink>
// etc
I have it connected to a BrowserRouter which correctly changes to the pages that are passed into the <Route>'s, so I believe I have set this all up correctly.
<div className="App">
<Navbar />
<Route path="/about" component={About} />
<Route exact path="/" component={Home} />
</div>
I have the class 'active' in my App.css file to change the button font color to red. This works, but the issue for me is that the /home link never loses its active class. Example image:
[![enter image description here][1]][1]
Am I missing something obvious here?
[1]: https://i.stack.imgur.com/w3rHD.png
I don't know if I'm using the same version as you are but I've always used isActive on NavLinks to determine if they are active or not.
<NavLink to="/" isActive={(match, location) => {
return location.pathname === "/";
}} activeClassName="active">Home</NavLink>
Just did a quick search and found this which might be helpful: https://reactrouter.com/web/api/NavLink
You need to set exact for the root <NavLink/> too,
<NavLink exact to="/">Home</NavLink>
like so ^
https://codesandbox.io/s/react-router-sidebar-forked-dd724?file=/example.js:1210-1279
I have a React app in which a search options is included in the navbar, and can be accessed anywhere in the app. Whenever a user searches and then clicks on one of the products show, it should send them to a detail view of that product.
The problem I am facing is with routing. Whenever I search and click in the product for the first time, it correctly redirects to, as an example, http://localhost:3000/catalogue/women/sportive-shoes-sneakers/sneakers--low-top-sneaker/292487. However, if once I am in that route I try to go send the user to another product, it formulates the url like this: http://localhost:3000/catalogue/women/sportive-shoes-sneakers/sneakers--low-top-sneaker/catalogue/men/denim-pants/jeans/293140. As you can see, it's nesting the pathnames one after the other.
My code looks like this:
this.state.displayProducts.map((product, index) => {
return (
<NavLink strict to={{
pathname: `catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`,
selectedProduct: product
}} key={index}>
<div className="catalogue-productlist-product" onClick={this.props.wipeInput && this.props.wipeInput}>
<img src={product.images && product.images[0] ? product.images[0].replace('{authorization}', this.props.token) : this.props.placeholder}
alt="Category" onError={this.imgError} />
<div>
{product.productName}
</div>
</div>
</NavLink>
)
})
And my Route like this:
<Route
exact path="/catalogue"
render={(props) => (
window.location.search.length > 0 ?
<ProductList />
:
<Slider
{...props}
categories={this.props.categories}
/>
)}
/>
<Route
exact path={`/catalogue/search/:qs`}
component={ProductList}
/>
<Route
exact path={`/catalogue/:line/:family?/`}
render={(props) => (
<Slider
{...props}
categories={this.props.categories}
/>
)} />
<Route
exact path={`/catalogue/:line/:family/:subfamily`}
component={ProductList} />
<Route
exact path={`/catalogue/:line/:family/:subfamily/:product`}
component={ProductDetail} />
How would one go about it working as intended? ie: no matter the current route, the user should always be sent to wherever the navlink send them.
The problem lies here in your pathname, you should always consider add a leading slash in your pathnames just like this:
...
pathname: `/catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`
...
If you don't use this leading slash in your link navigation it will always append the path you provided to the previous path that you provided, so it will make your first redirect nice and tidy then the upcoming ones will append to the existing path that you are in right now.
NOTE: If you always want to change the whole directory with each product selected consider adding leading slash in your pathname, otherwise, you should use an alternative way.
I think i know what the problem is, I am not 100% sure but going to go out on a limb, because I have encountered similar issues in the past, I think you need to add a '/' to the pathname value before 'catalogue'.
...
return (
<NavLink strict to={{
pathname: `/catalogue/${StringUtil.getUrlFromString(product.line)}/${StringUtil.getUrlFromString(product.familyName)}/${StringUtil.getUrlFromString(product.subfamilyName)}/${product.id[0] || product.id}`,
...
Because it is not prefixed with '/', it is appending the path to the current path rather than starting it from the beginning.
I have a simple react website with some transitions between sites. It works fine but when I refresh website it doesn't work anymore as the url do not match.
I mean - for example inital url is user.github.io/app and when I proceed to pageOne the expected url should be user.github.io/app/pageOne but the url is just user.github.io/pageOne. I've tried HashRouter with basename but to be honest im quite new to React and I do not understand it. HashRouter gives me this strange /#/ which doesn't look to good on a webiste. Any quick way to fix it?
You have asked for code, so here it is (can't post comments that long)
{transitions.map(({ item, props, key }) => (
<animated.div key={key} style={props}>
<Switch location={item}>
<Route path="/react-portfolio" component={PageOne} />
<Route path="*/pathfinding" component={PageOne} />
<Route path="*/easing" component={PageTwo} />
<Route path="*/drinks" component={PageThree} />
</Switch>
</animated.div>
This is the example page transition from Spring docs. There is no router to be honest. Here is the one link code (rest is the same)
<NavLink to="/pathfinding" className="nav-item nav-link" activeClassName="nav-item nav-link active">
Pathfinding