在重新加载页面之前,react-transition-group 无法工作
我用 React Router 5 和 React Redux 制作了一个简单的 TaskShceduler,并且仍然可以正常工作。
现在我尝试使用react-transition-group 在不同路线之间设置动画。
如果我单击网址已更改,但屏幕不会重新呈现,直到我手动重新加载页面。
动画仅在我使用浏览器上的后退按钮时才起作用,
我不会在控制台上收到错误消息。
问题是什么以及解决方案是什么?
谢谢
App.js
import { Fragment, useEffect, Suspense } from "react";
import { useSelector, useDispatch } from "react-redux";
import Notification from "./shared/UIElements/Notification";
import { sendCartData, fetchCartData } from "./store/cart-actions";
import Auth from "./Auth/page/Auth";
import TaskMain from "./tasks/page/TaskMain";
import TaskFilter from "./tasks/page/TaskFilter";
import MainNavigation from "./Layout/Navigation/MainNavigation";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import "../src/scss/styles.css";
import UpdateTask from "./tasks/page/UpdateTask";
import {
BrowserRouter as Router,
Route,
Redirect,
Switch,
useLocation,
} from "react-router-dom";
import NewTask from "./tasks/page/NewTask";
let isInitial = true;
function App() {
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
const logged = useSelector((state) => state.cart.logged);
const location = useLocation();
useEffect(() => {
dispatch(fetchCartData());
console.log(`fetch usefeect`);
}, [dispatch]);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
if (cart.changed) {
dispatch(sendCartData(cart));
}
}, [cart, dispatch]);
let routes;
if (!logged) {
routes = (
<Switch location={location}>
<Route path="/" exact>
<Auth />
</Route>
<Redirect to="/" />
</Switch>
);
} else {
routes = (
<Switch location={location}>
<Route path="/" exact>
<TaskMain />
</Route>
<Route path="/tasks/new" exact>
<NewTask />
</Route>
<Route path="/tasks/update/:id" exact>
<UpdateTask />
</Route>
<Route path="/items" exact>
<TaskFilter />
</Route>
<Redirect to="/" />
</Switch>
);
}
return (
<Router>
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainNavigation />
<TransitionGroup>
<CSSTransition
timeout={1250}
classNames='fade'
key={location.key}
>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
{routes}
</Suspense>
</CSSTransition>
</TransitionGroup>
</Fragment>
</Router>
);
}
export default App;
App.css
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
transition: opacity 1250ms ease-in;
opacity: 0;
}
MainNavigation.js
import React, { useState } from 'react';
import NavLinks from './NavLinks';
import SideDrawer from './SideDrawer';
import Backdrop from '../UIElements/Backdrop';
const MainNavigation = (props) => {
const [drawerIsOpen, setDrawerIsOpen] = useState(false);
const openDrawerHandler = () => {
setDrawerIsOpen(true);
};
const closeDrawerHandler = () => {
setDrawerIsOpen(false);
};
return (
<React.Fragment>
{drawerIsOpen && <Backdrop onClick={closeDrawerHandler} />}
<SideDrawer show={drawerIsOpen} onClick={closeDrawerHandler}>
<nav className="main-navigation__drawer-nav">
<NavLinks />
</nav>
</SideDrawer>
<header className="main-header header-grid">
<button
className="main-navigation__menu-btn"
onClick={openDrawerHandler}
>
<span />
<span />
<span />
</button>
<div className="title-grid">
<h1 className="main-navigation__title fontsize-18">TODO APP</h1>
</div>
<div className="links-grid">
<NavLinks />
</div>
</header>
</React.Fragment>
);
};
export default MainNavigation;
NavLink.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import {useHistory } from "react-router-dom";
import { cartActions } from "../../store/cart-slice";
const NavLinks = (props) => {
const logged = useSelector((state) => state.cart.logged);
const history = useHistory();
const dispatch = useDispatch();
const foOldalHandler = () => {
history.push(`/`);
};
const szuresHandler = () => {
history.push(`/items`);
};
const logoutHanler = () => {
dispatch(cartActions.logout());
};
return (
<React.Fragment>
<div>
<nav className="nav-links">
<button className="nav-links-button" onClick={foOldalHandler}>Főoldal</button>
{logged && <button className="nav-links-button" onClick={szuresHandler}>Szűrés</button>}
{logged && <button className="nav-links-button" onClick={logoutHanler}>Kijelentkezés</button>}
</nav>
</div>
</React.Fragment>
);
};
export default NavLinks;
SideDrawer.js
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
const SideDrawer = props => {
const content = (
<CSSTransition
in={props.show}
timeout={200}
classNames="slide-in-left"
mountOnEnter
unmountOnExit
>
<aside className="side-drawer" onClick={props.onClick}>{props.children}</aside>
</CSSTransition>
);
return ReactDOM.createPortal(content, document.getElementById('drawer-hook'));
};
export default SideDrawer;
index.js
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/index';
import './index.css';
import App from './App';
import {
BrowserRouter,
BrowserRouter as Router,
} from "react-router-dom";
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
NewTask.js
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { cartActions } from "../../store/cart-slice";
import { v4 as uuid } from "uuid";
export default function NewTask() {
const dispatch = useDispatch();
const username = useSelector((state) => state.cart.username);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [status, setStatus] = useState("aktiv");
const history = useHistory();
const usernameChangeHandler = (event) => {
setTitle(event.target.value);
};
const descriptionChangeHandler = (event) => {
setDescription(event.target.value);
};
const statusChangeHandler = (event) => {
setStatus(event.target.value);
};
const newTaskHandler = (event) => {
event.preventDefault();
console.log(`title: ${title} description ${description} status: ${status}`);
dispatch(
cartActions.addTask({
id: uuid(),
username,
title,
description,
status,
})
);
history.push("/");
};
return (
<div>
<div className='main-introduction'>
<h2 >Hello {username}!</h2>
<h1>Milyen feladatot szeretnél elvégezni?</h1>
</div>
<form onSubmit={newTaskHandler}>
<label htmlFor="title">A feladat címe</label>
<br></br>
<input
id="title"
type="text"
value={title}
onChange={usernameChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="description">A feladat leírása:</label>
<br></br>
<input
id="description"
type="text"
value={description}
onChange={descriptionChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="status">A feladat állapotta:</label>
<br></br>
<div onChange={statusChangeHandler}>
<input type="radio" value="aktiv" name="status" required /> Aktív
<input type="radio" value="kesz" name="status" /> Teljesített
</div>
<br></br>
<button type="submit">Hozzáad</button>
</form>
</div>
);
}
package.json
"dependencies": {
"@reduxjs/toolkit": "^1.5.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.5.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "5.3.0",
"react-scripts": "4.0.1",
"react-spring": "^9.4.3",
"react-transition-group": "^4.4.2",
"use-sound": "^4.0.1",
"uuidv4": "^6.2.12",
"web-vitals": "^0.2.4"
},
I made an simple taskshceduler with react router 5 and react redux and work fine still.
Now I try animating between different route with react-transition-group.
If I click the url changed BUT the screen not re-render until i reload the page mannualy.
Th animation only works when i use the backward button on the browser
I not get error to the console.
What is the problem and What is the solution?
Thank you
App.js
import { Fragment, useEffect, Suspense } from "react";
import { useSelector, useDispatch } from "react-redux";
import Notification from "./shared/UIElements/Notification";
import { sendCartData, fetchCartData } from "./store/cart-actions";
import Auth from "./Auth/page/Auth";
import TaskMain from "./tasks/page/TaskMain";
import TaskFilter from "./tasks/page/TaskFilter";
import MainNavigation from "./Layout/Navigation/MainNavigation";
import LoadingSpinner from "./shared/UIElements/LoadingSpinner";
import TransitionGroup from "react-transition-group/TransitionGroup";
import CSSTransition from "react-transition-group/CSSTransition";
import "../src/scss/styles.css";
import UpdateTask from "./tasks/page/UpdateTask";
import {
BrowserRouter as Router,
Route,
Redirect,
Switch,
useLocation,
} from "react-router-dom";
import NewTask from "./tasks/page/NewTask";
let isInitial = true;
function App() {
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const notification = useSelector((state) => state.ui.notification);
const logged = useSelector((state) => state.cart.logged);
const location = useLocation();
useEffect(() => {
dispatch(fetchCartData());
console.log(`fetch usefeect`);
}, [dispatch]);
useEffect(() => {
if (isInitial) {
isInitial = false;
return;
}
if (cart.changed) {
dispatch(sendCartData(cart));
}
}, [cart, dispatch]);
let routes;
if (!logged) {
routes = (
<Switch location={location}>
<Route path="/" exact>
<Auth />
</Route>
<Redirect to="/" />
</Switch>
);
} else {
routes = (
<Switch location={location}>
<Route path="/" exact>
<TaskMain />
</Route>
<Route path="/tasks/new" exact>
<NewTask />
</Route>
<Route path="/tasks/update/:id" exact>
<UpdateTask />
</Route>
<Route path="/items" exact>
<TaskFilter />
</Route>
<Redirect to="/" />
</Switch>
);
}
return (
<Router>
<Fragment>
{notification && (
<Notification
status={notification.status}
title={notification.title}
message={notification.message}
/>
)}
<MainNavigation />
<TransitionGroup>
<CSSTransition
timeout={1250}
classNames='fade'
key={location.key}
>
<Suspense
fallback={
<div className="center">
<LoadingSpinner></LoadingSpinner>
</div>
}
>
{routes}
</Suspense>
</CSSTransition>
</TransitionGroup>
</Fragment>
</Router>
);
}
export default App;
App.css
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
transition: opacity 1250ms ease-in;
opacity: 0;
}
MainNavigation.js
import React, { useState } from 'react';
import NavLinks from './NavLinks';
import SideDrawer from './SideDrawer';
import Backdrop from '../UIElements/Backdrop';
const MainNavigation = (props) => {
const [drawerIsOpen, setDrawerIsOpen] = useState(false);
const openDrawerHandler = () => {
setDrawerIsOpen(true);
};
const closeDrawerHandler = () => {
setDrawerIsOpen(false);
};
return (
<React.Fragment>
{drawerIsOpen && <Backdrop onClick={closeDrawerHandler} />}
<SideDrawer show={drawerIsOpen} onClick={closeDrawerHandler}>
<nav className="main-navigation__drawer-nav">
<NavLinks />
</nav>
</SideDrawer>
<header className="main-header header-grid">
<button
className="main-navigation__menu-btn"
onClick={openDrawerHandler}
>
<span />
<span />
<span />
</button>
<div className="title-grid">
<h1 className="main-navigation__title fontsize-18">TODO APP</h1>
</div>
<div className="links-grid">
<NavLinks />
</div>
</header>
</React.Fragment>
);
};
export default MainNavigation;
NavLink.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import {useHistory } from "react-router-dom";
import { cartActions } from "../../store/cart-slice";
const NavLinks = (props) => {
const logged = useSelector((state) => state.cart.logged);
const history = useHistory();
const dispatch = useDispatch();
const foOldalHandler = () => {
history.push(`/`);
};
const szuresHandler = () => {
history.push(`/items`);
};
const logoutHanler = () => {
dispatch(cartActions.logout());
};
return (
<React.Fragment>
<div>
<nav className="nav-links">
<button className="nav-links-button" onClick={foOldalHandler}>Főoldal</button>
{logged && <button className="nav-links-button" onClick={szuresHandler}>Szűrés</button>}
{logged && <button className="nav-links-button" onClick={logoutHanler}>Kijelentkezés</button>}
</nav>
</div>
</React.Fragment>
);
};
export default NavLinks;
SideDrawer.js
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
const SideDrawer = props => {
const content = (
<CSSTransition
in={props.show}
timeout={200}
classNames="slide-in-left"
mountOnEnter
unmountOnExit
>
<aside className="side-drawer" onClick={props.onClick}>{props.children}</aside>
</CSSTransition>
);
return ReactDOM.createPortal(content, document.getElementById('drawer-hook'));
};
export default SideDrawer;
index.js
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store/index';
import './index.css';
import App from './App';
import {
BrowserRouter,
BrowserRouter as Router,
} from "react-router-dom";
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
);
NewTask.js
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { cartActions } from "../../store/cart-slice";
import { v4 as uuid } from "uuid";
export default function NewTask() {
const dispatch = useDispatch();
const username = useSelector((state) => state.cart.username);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [status, setStatus] = useState("aktiv");
const history = useHistory();
const usernameChangeHandler = (event) => {
setTitle(event.target.value);
};
const descriptionChangeHandler = (event) => {
setDescription(event.target.value);
};
const statusChangeHandler = (event) => {
setStatus(event.target.value);
};
const newTaskHandler = (event) => {
event.preventDefault();
console.log(`title: ${title} description ${description} status: ${status}`);
dispatch(
cartActions.addTask({
id: uuid(),
username,
title,
description,
status,
})
);
history.push("/");
};
return (
<div>
<div className='main-introduction'>
<h2 >Hello {username}!</h2>
<h1>Milyen feladatot szeretnél elvégezni?</h1>
</div>
<form onSubmit={newTaskHandler}>
<label htmlFor="title">A feladat címe</label>
<br></br>
<input
id="title"
type="text"
value={title}
onChange={usernameChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="description">A feladat leírása:</label>
<br></br>
<input
id="description"
type="text"
value={description}
onChange={descriptionChangeHandler}
className='input-width'
required
/>
<br></br>
<label htmlFor="status">A feladat állapotta:</label>
<br></br>
<div onChange={statusChangeHandler}>
<input type="radio" value="aktiv" name="status" required /> Aktív
<input type="radio" value="kesz" name="status" /> Teljesített
</div>
<br></br>
<button type="submit">Hozzáad</button>
</form>
</div>
);
}
package.json
"dependencies": {
"@reduxjs/toolkit": "^1.5.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.5.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-redux": "^7.2.2",
"react-router-dom": "5.3.0",
"react-scripts": "4.0.1",
"react-spring": "^9.4.3",
"react-transition-group": "^4.4.2",
"use-sound": "^4.0.1",
"uuidv4": "^6.2.12",
"web-vitals": "^0.2.4"
},
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我想找到了答案。在 Drew Reese 的帮助
下组织我的路线组件不同的文件。
它会导致正确的渲染。
除此之外,使用 location.pathname 而不是 location.key。
如果您有更好的解决方案请写在这里
谢谢
App.js
AuthenticationApp.js
TaskApp.js
I think found the answer. with help for Drew Reese
Organize my route components different file.
It cause the right rendering.
Besides that using location.pathname instead location.key.
If you have better solution please write here
Thank you
App.js
AuthenticationApp.js
TaskApp.js