exitBeforeEnter is not working as intended - Trouble w/ Page Transitions - html

I'm trying to create a smooth page transition using a combination of react-router-dom and framer-motion, and I'm trying to have my pages fade out on exit and fade in on enter. But exitBeforeEnter is not working how it's supposed to. The page will not fade out on exit but the next page will fade in every time. Below is my code, and I'll attach one of the page files (all of the pages have pretty much the same code).
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));
App.js
import React from 'react';
import About from "./pages/About.js"
import Home from "./pages/Home.js"
import Projects from "./pages/Projects.js"
import { AnimatePresence } from 'framer-motion'
import { BrowserRouter as Switch, Route, useLocation } from "react-router-dom";
function App() {
const location = useLocation();
return (
<div className="App">
<AnimatePresence exitBeforeEnter>
<Switch location={location} key={location.pathname}>
<Route path="/about" component={About} />
<Route path="/projects" component={Projects} />
<Route path="/" exact component={Home} />
</Switch>
</AnimatePresence>
</div>
);
}
export default App;
Home.js (Page File)
import React from 'react';
import '../css/main.css';
import '../css/index.css';
import particleText from '../components/ParticleText.js'
import { Link } from 'react-router-dom';
import { motion } from 'framer-motion'
const pageVariants = {
in: {
opacity: 1,
transition: {
duration: 1
}
},
out: {
opacity: 0,
transition: {
duration: 1
}
}
}
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
try { particleText(true) } catch { /* Error */ }
return (
<motion.div className="main" initial="out" animate="in" exit="out" variants={pageVariants}>
<div className="introOverlay"></div>
<Link to="/about" className="homeButtonContainer">
<p className="homeButtonText">About Me</p>
</Link>
<Link to="/projects" className="homeButtonContainer">
<p className="homeButtonText">My Projects</p>
</Link>
</motion.div>
);
}
}
export default Home;

The property exitBeforeEnter is deprecated now, it must be replaced with mode='wait':
<AnimatePresence mode='wait'>
...
<AnimatePresence>
That way, the change between components will be seamless since the first component will unmount before loading the next one with its respective animations.

Its a bit late but in case you still looking...
The exit animation will NOT take place if AnimatePrecence it self is unmounting from the react tree.
Try this in your App.js :
return (
<Router>
<Route
render={ ({location}) => (
<AnimatePresence initial={ fase } exitBeforeEnter>
<Switch location={ location } key={ location.pathname }>
<Route
exact
path='/'
render={ () => <Home /> }
/>
<Route
exact
path='/projects'
render={ () => <Projects /> }
/>
<Route
exact
path='/about'
render={ () => <About/> }
/>
</Switch>
</AnimatePresence>
) }
/>
</Router>
);
You can leave the callback, render for eg, I just include it for clarity.
The initial={false} is just for disabling the initial animation for subsequent page reload;
If this doesn't work, make sure to keep the same variant structure the same in all your other components.

Related

The parameter's value don't display with TS

Goal:
Display the parameter's value in the webpage when you click on the text "DetailView"
Problem:
The value don't display and I am not sure if this tis the correct error for this case.
"The 'jsxFragmentFactory' compiler option must be provided to use JSX fragments with the 'jsxFactory' compiler option.(17016)"
How should I solve it?
Thank you!
Stackblitz:
https://stackblitz.com/edit/react-router-typescript-example-asrnpk?
Other info:
*Newbie in react TS
----------------------------
import * as React from 'react';
import { BrowserRouter as Router, Link, Route, Routes } from 'react-router-dom';
import { render } from 'react-dom';
import { Home, Foo, Bar } from './Pages';
import DetailView from './DetailView';
import './style.css';
class App extends React.Component {
render() {
return (
<Router>
<div>
<nav>
<Link to="/">Home</Link>
<br />
<Link to="/foo">Foo</Link>
<br />
<Link to="/bar">Bar</Link>
<br />
<Link to="/detailview/3}">DetailView</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/foo" element={<Foo />} />
<Route path="/bar" element={<Bar />} />
<Route path="/detailview/:p1?" element={<DetailView />} />
</Routes>
</div>
</Router>
);
}
}
render(<App />, document.getElementById('root'));
----------------
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
interface MatchParams {
id: string;
}
interface Props extends RouteComponentProps<MatchParams> {
// ...
}
const DetailView: React.FC<Props> = ({ match }: Props): JSX.Element => {
const params = match.params;
return <>{`Test "${params.id}"`}</>;
};
export default DetailView;
-------------------
import React from 'react';
export const Home = () => <h1>Home Page</h1>;
export const Foo = () => <h1>Foo Page</h1>;
export const Bar = () => <h1>Bar Page</h1>;

React Router is showing only one of the Switch components and does nothing when others are called

My routing component looks like this:
import { Link, Route, Switch } from 'react-router-dom';
import Nav from 'react-bootstrap/Nav'
import AllWorkouts from './Workout/AllWorkouts/AllWorkouts';
import WorkoutCreate from './Workout/WorkoutCreate/WorkoutCreate';
import WorkoutDetails from './Workout/WorkoutDetails/WorkoutDetails';
import WorkoutEdit from './Workout/WorkoutEdit/WorkoutEdit';
const Main = () => {
return (
<Switch>
<Route path="/workout/:id/edit" componnet={WorkoutEdit} />
<Route path="/workout/:id/details" componnet={WorkoutDetails} />
<Route path="/workout/create" componnet={WorkoutCreate} />
<Route path="/workout/all" component={AllWorkouts} />
</Switch>
);
}
export default Main;
I have included BrowserRouter in index.js and the only route that is matching is path="/workout/all". I cannot call any of the other routs with Link or directly in the URL.
When I call /workout/all I can see the component with all the other routes nothing happens.
Thank you!
Below is the other component and I do not see any misspelling error:
import { Link, Route, Switch } from 'react-router-dom';
import Button from 'react-bootstrap/Button'
const AllWorkouts = () => {
return (
<div>
<Link to="/workout/create">
<Button variant="outline-primary">Create new workout</Button>{' '}
</Link>
<h1>Hello from AllWorkouts</h1>
</div>
);
}
export default AllWorkouts;
You have misspelled component in the other routes
mport { Link, Route, Switch } from 'react-router-dom';
import Nav from 'react-bootstrap/Nav'
import AllWorkouts from './Workout/AllWorkouts/AllWorkouts';
import WorkoutCreate from './Workout/WorkoutCreate/WorkoutCreate';
import WorkoutDetails from './Workout/WorkoutDetails/WorkoutDetails';
import WorkoutEdit from './Workout/WorkoutEdit/WorkoutEdit';
const Main = () => {
return (
<Switch>
<Route path="/workout/:id/edit" component={WorkoutEdit} />
<Route path="/workout/:id/details" component={WorkoutDetails} />
<Route path="/workout/create" component={WorkoutCreate} />
<Route path="/workout/all" component={AllWorkouts} />
</Switch>
);
}
export default Main;

Unexpected re-rendering of components

I'm trying to navigate back to a Menu component, but when I trigger a route to do so, the previous rendered Practice component is rendered in the root domain level, which should just be for the Menu component.
The solutions for similar questions on StackOverflow say to use exact in the route, but as you can see I have this in place.
How do I make this work as expected? Thanks.
Here's what I have...
App.js
import React from 'react';
import './App.css';
import PracticeContextProvider from './contexts/PracticeContext';
import {BrowserRouter as Router, Route, Switch, Link} from 'react-router-dom';
import Menu from './pages/Menu';
function App() {
return (
<div className="app">
<PracticeContextProvider>
<Menu />
</PracticeContextProvider>
</div>
);
}
export default App;
Menu.js
import React, { useContext } from 'react';
import {BrowserRouter as Router, Route, Switch, Link, useParams, useLocation, useRouteMatch} from 'react-router-dom';
import { PracticeContext } from '../contexts/PracticeContext';
import Practice from './Practice';
import ModuleLanguageSelector from '../components/ModuleLanguageSelector';
import ModuleListMenuItem from '../components/ModuleListMenuItem';
const Menu = () => {
const { modulesJson } = useContext(PracticeContext);
return (
<div>
<h1>Menu</h1>
<Router>
<Switch>
<Route exact path="/">
<ModuleLanguageSelector />
{ modulesJson && (
modulesJson.map(module => {
return (
<Link to={'/Practice/' + module._id} key={ module._id }>
<ModuleListMenuItem module={ module }></ModuleListMenuItem>
</Link>
)
})
)}
</Route>
<Route exact path="/Practice/:moduleId" component={Practice} />
</Switch>
</Router>
</div>
);
}
export default Menu;
Practice.js
import React, { useContext } from 'react';
import {BrowserRouter as Router, Route, Switch, Link, useParams, useLocation, useRouteMatch} from 'react-router-dom';
import { PracticeContext } from '../contexts/PracticeContext';
import Menu from './Menu';
import ModulePracticeAnswerArea from '../components/ModulePracticeAnswerArea';
import ModulePracticeQuestion from '../components/ModulePracticeQuestion';
import ModulePracticeProgress from '../components/ModulePracticeProgress';
import ModulePracticeTutorial from '../components/ModulePracticeTutorial';
const Practice = () => {
const { moduleId } = useParams();
const { questionIndex } = useContext(PracticeContext);
return (
<Router>
<div className="module-practice-screen">
<h1>Practice</h1>
<div className="module-practice-container">
<Link to={'/'}>
<button className="quit-practice">X</button>
</Link>
{
moduleId ?
<React.Fragment>
{/*<ModulePracticeTutorial moduleId={ moduleId } />*/}
<ModulePracticeProgress questionNumber={ questionIndex } />
<ModulePracticeQuestion moduleId={ moduleId } questionNumber={ questionIndex } />
<ModulePracticeAnswerArea moduleId={ moduleId } questionNumber={ questionIndex } />
</React.Fragment>
:
<h3>The menu should appear here!</h3>
}
</div>
</div>
<Switch>
<Route exact path="/" component={Menu} />
</Switch>
</Router>
);
};
export default Practice;
You don't need <Router> and <Switch> in Practice
const Practice = props => {
const { moduleId } = useParams();
const { questionIndex } = useContext(PracticeContext);
return (
<div>
<div className="module-practice-screen">
<h1>Practice</h1>
<div className="module-practice-container">
<Link to="/">
<button>X</button>
</Link>
{moduleId ? (
<React.Fragment>
{/*<ModulePracticeTutorial moduleId={ moduleId } />*/}
<ModulePracticeProgress questionNumber={questionIndex} />
<ModulePracticeQuestion
moduleId={moduleId}
questionNumber={questionIndex}
/>
<ModulePracticeAnswerArea
moduleId={moduleId}
questionNumber={questionIndex}
/>
</React.Fragment>
) : (
<h3>The menu should appear here!</h3>
)}
</div>
</div>
</div>
);
};
codesandbox

Going to another route must unmount current route component

Here is my use case, I made a simple example in order to ask this question, let's say I have two routes, /view1 and /view2, each mounting a component, View1 and View2, I need View1 to unmount when I go to /view2.
Here is the code if you want to try:
import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, Link, browserHistory} from 'react-router';
import Portal from 'react-portal';
class View1 extends React.Component {
constructor(context, props) {
super(context, props)
}
componentDidMount() {
console.log('view 1 mounted');
}
componentWillUnmout() {
console.log('view 1 unmount');
}
render() {
return (
<h2>hanta</h2>
);
}
}
class View2 extends React.Component {
constructor(context, props) {
super(context, props)
}
componentDidMount() {
console.log('view 2 mounted');
}
componentWillUnmout() {
console.log('view 2 unmount');
}
render() {
return (
<h2>View 2</h2>
);
}
}
class View extends React.Component {
render() {
return (
<div>
<h1>View</h1>
<Link to="/view1">view 1</Link>
<Link to="/view2">view 2</Link>
{this.props.children}
</div>
);
}
}
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={View}>
<Route path="/view1" component={View1} />
<Route path="/view2" component={View2} />
</Route>
</Router>
, document.getElementById("container"))
ComponentWillUnmout doesn't get called in any case, I'm using:
├── react#0.14.6
├── react-dom#0.14.6
├── react-router#2.0.0-rc5
You spelled it wrong. It should be componentWillUnmount, not componentWillUnmout.

Simple React router not working

Can't for the life of me get a simple react-router to work! It displays the first page, but when I click the link it doesn't do anything but change the url to /home. It keeps showing "app". Why is the home component not loading???
Simple code:
import React from 'react';
import { Link } from 'react-router'
import { render } from 'react-dom'
import { Router, Route } from 'react-router'
class App extends React.Component {
render() {
return (
<div><Link to="/home">app</Link></div>
);
}
}
class Home extends React.Component {
render() {
return (
<div>Honey, I'm home!!!</div>
);
}
}
render(
<Router>
<Route path="/" component={App}>
<Route path="home" component={Home}/>
</Route>
</Router>,
document.getElementById('tempoot')
)
Because you haven't told it to render Home anywhere. You need to do something like (core thing you are missing is {this.props.children}):
import React from 'react';
import { Link } from 'react-router'
import { render } from 'react-dom'
import { Router, Route } from 'react-router'
class App extends React.Component {
render() {
return (
<div>
<div className="nav">
<Link to="/home">app</Link>
</div>
<div className="content">
// THIS IS THE CORE LINE YOU ARE MISSING
{this.props.children || "No one is home :("}
</div>
</div>
);
}
}
class Home extends React.Component {
render() {
return (
<div>Honey, I'm home!!!</div>
);
}
}
render(
<Router>
<Route path="/" component={App}>
<Route path="home" component={Home}/>
</Route>
</Router>,
document.getElementById('tempoot')
)