I am trying to create a transition effect between pages using react-router-dom. I am trying to delay the rendering of the routes component, but it doesn't seem to work.
I have the following code
const App = () => {
const location = useLocation();
const [displayLocation, setDisplayLocation] = useState(location);
const [transitionStage, setTransitionStage] = useState("slideIn");
useEffect(() => {
if (location !== displayLocation) setTransitionStage("slideOut");
}, [location, displayLocation]);
return (
<>
<Layout>
<ScrollToTop />
<TransitionAnimation
transitionStage={transitionStage}
onAnimationEnd={() => {
if (transitionStage === "slideIn") {
setTransitionStage("slideOut");
setDisplayLocation(location);
}
}}>
</TransitionAnimation>
<Routes location={displayLocation}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Layout>
</>
);
};
export default App;
const TransitionAnimation = styled.div`
width: 0px;
height: 100vh;
background: #000000;
position: fixed;
top:0;
left:0;
bottom:0;
right: 0;
z-index: 0;
animation: ${(props) => props.transitionStage} 1s linear;
#keyframes slideIn {
from {
width: 0%;
}
to {
width: 90%;
}
}
#keyframes slideOut {
from {
right: 0%;
}
to {
right: -10000px;
}
}
`;
I have tried the above code, but it doesn't change the component even if the route changes.
Please what better way can I achieve this.
Related
I am quite new to React and have been trying to create a custom sidebar component. Based on my understanding by going through different tutorials, this is what I have so far:
react-router-dom version: ^6.3.0
storybook: ^6.1.17
These are my components
sidebar.jsx
const SidebarNav = styled.nav`
background: #15171c;
width: 250px;
height: 100vh;
display: flex;
justify-content: center;
position: fixed;
top: 0;
right: ${({ sidebar }) => (sidebar ? "0" : "-100%")};
transition: 350ms;
z-index: 10;
`;
const SidebarWrap = styled.div`
width: 100%;
`;
const Sidebar = () => {
const [sidebar, setSidebar] = useState(true);
return (
<>
<IconContext.Provider value={{ color: "#fff" }}>
<SidebarNav sidebar={sidebar}>
<SidebarWrap>
{sideBarData.map((item, index) => {
return <SubMenu item={item} key={index} />;
})}
</SidebarWrap>
</SidebarNav>
</IconContext.Provider>
</>
);
};
export default Sidebar;
sidebardata.jsx
export const sideBarData = [ {
title: "Question 4",
path: "/test"
},
// {
// title: "Question 5",
// path: "/testing"
// }
];
sidebarsubmenu.jsx
import React, { useState } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
const SidebarLink = styled(Link)`
display: flex;
color: #e1e9fc;
justify-content: space-between;
align-items: center;
padding: 20px;
list-style: none;
height: 60px;
text-decoration: none;
font-size: 18px;
&:hover {
background: #252831;
border-left: 4px solid green;
cursor: pointer;
}
`;
const SidebarLabel = styled.span`
margin-left: 16px;
`;
const DropdownLink = styled(Link)`
background: #252831;
height: 60px;
padding-left: 3rem;
display: flex;
align-items: center;
text-decoration: none;
color: #f5f5f5;
font-size: 18px;
&:hover {
background: green;
cursor: pointer;
}
`;
const SubMenu = ({ item }) => {
const [subnav, setSubnav] = useState(false);
const showSubnav = () => setSubnav(!subnav);
return (
<>
<SidebarLink to={item.path}
onClick={item.subNav && showSubnav}>
<div>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</div>
<div>
{item.subNav && subnav
? item.iconOpened
: item.subNav
? item.iconClosed
: null}
</div>
</SidebarLink>
{subnav &&
item.subNav.map((item, index) => {
return (
<DropdownLink to={item.path} key={index}>
{item.icon}
<SidebarLabel>{item.title}</SidebarLabel>
</DropdownLink>
);
})}
</>
);
};
export default SubMenu;
BookSideBarLayout.jsx
import React from 'react';
import Sidebar from './BookSideBar';
function Layout(props) {
return (
<div>
<div style={{display: "flex"}}>
<Sidebar/>
</div>
</div>
);
}
export default Layout;
BookSidebarRoutes.jsx
import React from "react";
import {BrowserRouter, Route,Routes} from "react-router-dom";
import Support from "./Support";
import Layout from "./BookSideBarLayout";
function MyRoutes() {
return (
<BrowserRouter>
<Layout/>
<Routes>
{/* <Route path="/" element={<Sidebar/>}/> */}
<Route path="/test" element={<Support/>}/>
</Routes>
</BrowserRouter>
);
}
export default MyRoutes;
The story that I created was as below
import { storiesOf } from '#storybook/react';
import StoryRouter from 'storybook-react-router';
import MyRoutes from '../../src/components/BookSidebarRoutes';
// export default {
// title: 'Side Bar',
// //decorators : [(Story) => (<MemoryRouter><Story/></MemoryRouter>)]
// };
storiesOf('Params', module)
.addDecorator(StoryRouter())
.add('params', () => (
<MyRoutes/>
));
export const Default = () => <MyRoutes />;
After I run my story above, I keep getting the error as below:
A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.**
I also tried Switch in the MyRoutes component but that too didn't work. Any guidance in this will be really helpful. Thank you in advance
When mounting a component with a routing path I want to trigger a CSSTransition. My goal is to have the Component which is about to be unmounted persist long enough for the mounting component to fully transition in.
An analog equivalency would be sliding two cards over each other and only then remove the obscured/hidden card below.
import {
Switch,
BrowserRouter,
Route,
Redirect,
useLocation,
} from 'react-router-dom';
import {
CSSTransition,
TransitionGroup,
SwitchTransition,
} from 'react-transition-group';
import NavBar from './jsx/NavBar';
import Home from './jsx/Home';
import SomePage from './jsx/SomePage';
function App() {
let location = useLocation();
return (
<div className='App'>
<NavBar />
<TransitionGroup>
<SwitchTransition mode='in-out'>
<CSSTransition
key={location.key}
classNames='slide'
timeout={{
enter: 0,
exit: 500,
}}
>
<Switch>
<Route path='/' exact>
<Redirect to='/home' />
</Route>
<Route path='/home' component={Home} exact />
<Route path='/page' component={SomePage} />
</Switch>
</CSSTransition>
</SwitchTransition>
</TransitionGroup>
</div>
);
}
.navbar {
width: 100%;
position: fixed;
top: 0;
z-index: 99;
}
.home {
height: 100vh;
.some-page {
height: 100vh;
width: 100%;
transform: translateY(100vh);
background-color: red;
}
.animate {
position: absolute;
z-index: 1;
top: 0;
transition: transform 0.75s ease-in;
overflow: hidden;
}
.slide-enter-done {
transform: translateY(0vh);
Someone plese help, I am unabe to find errors in my code that led the react-router-dom into not working. When I click on any link, URL changes but view doesn't. I've read more than 40 threads and done research but I am unable to find my mistake by my own.
The App.js file
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Switch, Route, Redirect, Link } from 'react-router-dom';
import Header from '../components/Header.component';
import Contact from '../pages/Contact.page';
import DivineShop from '../pages/DivineShop.page';
import Events from '../pages/Events.page';
import Forums from '../pages/Forums.page';
import Home from '../pages/Home.page';
import Sadhanas from '../pages/Sadhanas.page';
let App = (props) => {
useEffect(() => {
document.querySelector('#body').style.backgroundColor = '#eee';
document.querySelector('#body').style.color = '#444';
document.querySelector('#body').style.fontFamily = 'aladin';
document.querySelector('#body').style.fontSize = '18px';
},[])
return (
<>
<Header />
<Router>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/forums' exact component={Forums} />
<Route path='/events' exact component={Events} />
<Route path='/sadhanas' exact component={Sadhanas} />
<Route path='/divineshop' exact component={DivineShop} />
<Route path='/contact' exact component={Contact} />
<Redirect to='/'/>
</Switch>
</Router>
</>
);
}
export default App;
The header Component
import React from 'react';
import styled from 'styled-components';
import Nav from './Nav.component';
const HeroSection = styled.section`
background-image: url(${props => props.backgroundImage});
height: 70vh;
width: 100vw;
`;
const Header = (props) => {
return (
<header>
<Nav />
<HeroSection backgroundImage={props.backgroundImage}/>
</header>
);
}
export default Header
The Nav Component
import React, { useState } from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import styled from 'styled-components';
import colors from '../configs/colors';
import Logo from '../images/Logo.svg';
import { FaBars } from 'react-icons/fa';
import { GrClose } from "react-icons/gr";
const StyledLink = styled(Link)`
text-decoration: none;
margin-bottom: .5em;
color: ${colors.black};
padding: .5em;
transition: all .3s ease-in;
`;
const StyledMenus = () => (
<Router>
<>
<StyledLink to='/'>Home</StyledLink>
<StyledLink to='/forums'>Forums</StyledLink>
<StyledLink to='/events'>Events</StyledLink>
<StyledLink to='/sadhanas'>Sadhanas</StyledLink>
<StyledLink to='/divineshop'>Divine Shop</StyledLink>
<StyledLink to='/contact'>Contact</StyledLink>
</>
</Router>
);
const MobileNav = styled.nav`
display: flex;
flex-direction: column;
flex-wrap: wrap;
aligh-items: flex-start;
width: 40%;
position: absolute;
left: 1em;
top: 3.5em;
box-shadow: -5px -5px 5px #f9f9f9, 5px 5px 5px #ccc;
border-radius: 20px;
padding: .5em .2em;
display: ${props => props.visibility ? 'flex' : 'none'};
#media screen and (min-width: 550px){
display: none;
}
& *:hover {
padding-left: 2em;
background: ${colors.chineseYellow};
color: ${colors.queenBlue};
}
& *:first-child {
border-radius: 10px 10px 0 0;
}
`;
const HighResNav = styled.nav`
display: flex;
flex-direction: row;
flex: 1 1 70%;
justify-content: space-evenly;
#media screen and (max-width: 550px){
display: none;
}
& * {
border-bottom: 2px solid ${colors.white};
}
& *:hover{
color: ${colors.queenBlue};
border-bottom-color: ${colors.queenBlue};
}
`;
const StyledFaBars = styled(FaBars)`
cursor: pointer;
font-size: 1.2em;
flex: 0 0 auto;
&:hover{
color: ${colors.queenBlue}
}
#media screen and (min-width: 550px){
display: none;
}
`;
const StyledGrClose = styled(GrClose)`
cursor: pointer;
font-size: 1.2em;
flex: 0 0 auto;
&:hover{
color: ${colors.queenBlue}
}
#media screen and (min-width: 550px){
display: none;
}
`;
const NavContainer = styled.section`
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 1em 2em;
justify-content: space-between;
align-items: center;
`;
const Nav = () => {
const [mobileMenuVisibility, setMobileMenuVisibility] = useState(false);
const handleBarClick = () => {
console.log(mobileMenuVisibility);
setMobileMenuVisibility(!mobileMenuVisibility);
}
return (
<NavContainer>
{mobileMenuVisibility ? <StyledGrClose onClick={handleBarClick} /> : <StyledFaBars onClick={handleBarClick} />}
<img src={Logo} alt="Main-Logo" />
<HighResNav>
<StyledMenus />
</HighResNav>
<MobileNav visibility={mobileMenuVisibility ? "true" : undefined}>
<StyledMenus />
</MobileNav>
</NavContainer>
)
}
export default Nav;
Every page have this structure
import React from 'react';
import {withRouter} from 'react-router-dom';
const Contact = () => {
return (
<div>
Hello from contact page
</div>
)
}
export default withRouter(Contact);
Please help
I suspect the issue is that you have two distinct Router components. They are not linked in any way - changing the url in one will not propagate that change to the Switch in the other. Remove the second Router from the StyledMenus component, and move the Header component inside the original Router in App.js.
<Router>
<Header />
<Switch>
<Route path='/' exact component={Home} />
<Route path='/forums' exact component={Forums} />
<Route path='/events' exact component={Events} />
<Route path='/sadhanas' exact component={Sadhanas} />
<Route path='/divineshop' exact component={DivineShop} />
<Route path='/contact' exact component={Contact} />
<Redirect to='/'/>
</Switch>
</Router>
I just created a react app. First thing first, I wanted to make a navbar for the left side that will be accessible on every page. So far so good, it's working well, my issue arrises when I started to create my first page: it keeps clipping under my navbar, and nothing I do gets it out of under the bar, this is driving me insane. Here's the current state of the code...
App.js
class App extends Component {
render() {
return(
<Router>
<SideNavBar />
<Switch>
<Route exact path={"/"} component={HomePage} />
</Switch>
</Router>
);
}
}
Navbar
class SideNavBar extends Component {
constructor(props) {
super(props);
this.state = {
currentPath: props.location.pathname,
};
}
onClick (path) {
this.setState({ currentPath: path });
}
render() {
const { currentPath } = this.state;
const navItems =
[
{
path: "/",
css: "fas fa-home"
}, {
path: "/user",
css: "fas fa-user"
},
];
return(
<StyledSideNavBar>
{
navItems.map((item, index) => {
return (
<NavItem
item={item}
active={item.path === currentPath}
onClick={this.onClick.bind(this)}
key={index}
/>
);
})
}
</StyledSideNavBar>
);
}
}
Styled Navbar
const StyledSideNavBar = styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
position: fixed;
height: 100vh;
width: 5rem;
top: 0;
left: 0;
padding-top: 1.5rem;
background-color: #EEEEEE;
`;
Navitem
class NavItem extends Component {
render() {
const { item, active, onClick } = this.props;
return(
<StyledNavItem active={active}>
<Link to={item.path} className={item.icon} onClick={() => onClick(item.path)} />
</StyledNavItem>
);
}
}
Styled Navitem
const StyledNavItem = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
margin-bottom: 1.5rem;
a {
font-size: 2.7em;
color: ${(props) => props.active ? "#8394F5" : "black"};
:hover {
opacity: 0.7;
text-decoration: none;
}
}
`;
HomePage
class HomePage extends Component {
render() {
return (
<StyledHomePage>
{"Hi {user}!hhhhhhhhhhhhhhhhhhhhhh"}
</StyledHomePage>
);
}
}
Styled HomePage
const StyledHomePage = styled.div`
display: "flex",
margin: "5rem 5rem 0 5rem"
`;
The problem arises when you give postion: fixed to your NavBar, instead you should create a fluid design and remove fixed position. Let me know if you need more help in it.
I have written a simple search component with autosuggestion in react, it makes calls to the themoviedb. I am using react-router-dom and have defined a route param like this in app.js:
<Route path="/:id" component={SomeComponent} />
and the search component looks like this:
import React, { Component, Fragment } from "react";
import { Link } from "react-router-dom";
import styled from "styled-components";
import axios from "axios";
const SuggestionsResult = styled.ul`
text-decoration: none;
list-style: none;
text-align: left;
display: flex;
flex-direction: column;
width: 100%;
`;
const ResultItem = styled.li`
border-bottom: 0.0625rem solid hsla(0, 0%, 100%, 0.12);
padding: 10px 0 10px;
padding-left: 2px;
font-size: 1em;
cursor: pointer;
&:hover {
background: hsla(0, 0%, 100%, 0.12);
}
`;
export default class Search extends Component {
state = {
query: "",
results: [],
showSuggestions: false
};
handleInputChange = () => {
this.setState(
{
query: this.search.value
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${
this.state.query
}&page=1&include_adult=false`
)
.then(({ data }) => {
this.setState({
results: data.results,
showSuggestions: !this.state.showSuggestions
});
});
}
} else if (!this.state.query) {
}
}
);
};
handleSuggestionClick = e => {
this.setState({ showSuggestions: false });
};
render() {
return (
<Fragment>
<input
placeholder="Search for a movie..."
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
{this.state.showSuggestions && (
<Suggestions
results={this.state.results}
handleSuggestionClick={this.handleSuggestionClick}
/>
)}
</Fragment>
);
}
}
const Suggestions = ({ results, handleSuggestionClick }) => {
const options = results.map(r => (
<ResultItem key={r.id}>
<Link onClick={handleSuggestionClick} to={`/${r.id}`}>
{r.title}
</Link>
</ResultItem>
));
return <SuggestionsResult>{options}</SuggestionsResult>;
};
My problem is when clicking on the link it changes the url but it stays on the same site. If I dont use react-router-dom Link component and only use a elements it works fine but everthing re-renders.
** Update
My react-router code in app.js
<Router>
<Switch>
<Route exact path="/" component={MoviesList} />
<Route path="/:id" component={MovieDetail} />
</Switch>
</Router>
The Suggestions component doesn't receive the Router prop and hence it causes this issue. Wrap your Suggestions component with withRouter HOC. Also make sure that the Search component is rendered as a child or Router component
import React, { Component, Fragment } from "react";
import { Link, withRouter } from "react-router-dom";
import styled from "styled-components";
import axios from "axios";
const SuggestionsResult = styled.ul`
text-decoration: none;
list-style: none;
text-align: left;
display: flex;
flex-direction: column;
width: 100%;
`;
const ResultItem = styled.li`
border-bottom: 0.0625rem solid hsla(0, 0%, 100%, 0.12);
padding: 10px 0 10px;
padding-left: 2px;
font-size: 1em;
cursor: pointer;
&:hover {
background: hsla(0, 0%, 100%, 0.12);
}
`;
export default class Search extends Component {
state = {
query: "",
results: [],
showSuggestions: false
};
handleInputChange = () => {
this.setState(
{
query: this.search.value
},
() => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${apiKey}&language=en-US&query=${
this.state.query
}&page=1&include_adult=false`
)
.then(({ data }) => {
this.setState({
results: data.results,
showSuggestions: !this.state.showSuggestions
});
});
}
} else if (!this.state.query) {
}
}
);
};
handleSuggestionClick = e => {
this.setState({ showSuggestions: false });
};
render() {
return (
<Fragment>
<input
placeholder="Search for a movie..."
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
{this.state.showSuggestions && (
<Suggestions
results={this.state.results}
handleSuggestionClick={this.handleSuggestionClick}
/>
)}
</Fragment>
);
}
}
const Suggestions = withRouter(({ results, handleSuggestionClick }) => {
const options = results.map(r => (
<ResultItem key={r.id}>
<Link onClick={handleSuggestionClick} to={`/${r.id}`}>
{r.title}
</Link>
</ResultItem>
));
return <SuggestionsResult>{options}</SuggestionsResult>;
});