这是一个关于React路由器及其设计的问题,特别是在考虑页面转换时。
React专为模块化而设计,在该领域表现出色。但是我发现在维持组件之间的隔离性和模块化的同时,似乎很难完成一项任务:页面过渡。
我确实在Medium和StackOverFlow等网站上阅读了许多与此相关的文章和问题;但是到目前为止,我还没有阅读任何解决此问题的好的架构方法的描述,也没有解释为什么React Router不考虑这种常见情况。
因此,让我们考虑一个包含三个页面(PageA,PageB和PageC)的网站:
如果我们要使用相同的动画来安装/卸载这三个页面,假设使用FadeIn进行安装和使用FadeOut进行卸载,则可以使用React Transition Group组件包装我们的<Switch>
组件,它将负责使用CSS过渡为页面设置动画。这里的CSS过渡将由<CSSTransition>
组件控制,并将类传递给ClassNames
。
<div className="Main">
<TransitionGroup component={null}>
<CSSTransition
key={locationKey}
appear={true}
classNames="Main"
timeout={{
enter: 300,
exit: 300,
}}
>
<Switch location={this.props.location}>
<Route
path="/PageA"
render={(props) => {
return <PageA />;
}}
/>
<Route
path="/PageB"
render={(props) => {
return <PageA />;
}}
/>
<Route
path="/PageC"
render={(props) => {
return <PageA />;
}}
/>
</Switch>
</CSSTransition>
</TransitionGroup>
</div>
一切正常,我们很高兴,我们的站点上有一些漂亮的动画,可以进行装卸。
现在,我们要为每个页面的每个安装/卸载操作进行不同的转换:要使用FadeIn进行安装的PageA和使用FadeOut进行卸载的页面A,使用从左向右滑动的PageB以及卸载的滑动回到左侧,然后将PageC从底部安装到顶部,然后从安装位置滑回底部。
我们将面临两个问题:如果使用<CSSTransition>
组件,则必须根据呈现的页面将不同的classNames
道具传递给它。我尚未使用此策略实施任何操作,因为还有一个与我有关的问题:如果遵循此路径,我们将在<CSSTransition>
组件内部存储用于对页面组件进行动画处理的逻辑,而这是在页面外部的。在我看来,这并不是很模块化和孤立,并且我阅读的有关软件设计,模块化和孤立的书越多,我对这种方法的困惑就越大。
在理想情况下,应在每个页面的主要组件中定义页面动画的逻辑。像这样:
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
classForAnimation: '',
};
}
componentDidMount() {
this.setState({
classForAnimation: ' FadeIn',
});
}
render() {
return <div className={'Home' + this.state.classForAnimation}>
I'm Home page
</div>;
}
}
export default Home;
还有一些CSS:
.Home {
opacity: 0;
transition: opacity 0.5s ease;
}
.FadeIn {
opacity: 1;
}
这仅用于安装,真的很容易。因此,如果这是一个更好的解决方案,那为什么不使用它呢?
原因是因为 React-router不了解我们的转换,它会卸载页面以立即进行动画处理,甚至不等待动画开始(当然也没有完成)。 <CSSTransition>
组件就是这样做的,但是将页面转换的逻辑集中在页面组件之外。
因此,考虑到多个页面在装入/卸载时具有不同动画的情况,这是我的问题:
A)是否有任何理由说明逻辑之间的转换逻辑 页面不应该存储在每个页面的主要组件内?
B)有什么理由可以将React-router设计为卸载 组件忽略等待不同类型事件的情况 在不同的页面?
C)有没有解决我不考虑的常见问题的方法?
D)可以从头开始编写自定义反应路由器吗?
尼克。
答案 0 :(得分:1)
您可以在此处使用链接组件来管理生命周期:
https://yeutech-lab.github.io/react-router-dom-utils/#link
安装:
npm install @yeutech-lab/react-router-dom-utils --save
用法:
export default (
<Link
component={(props) => <div {...props}>Hello world</div>}
waitChunk={true}
// You can pass routes as props if you use waitChunk
routes={[{ name: 'Home', component: Home, path: '/' }]}
// OR a ContextConsumer that own it
ContextConsumer={AppContextConsumer}
// use lifecycle callbacks
onClick={actionStartLoadingIndicator}
onPreload={() => console.log(`
chunk load in the background on mouseover or
when user click if waitChunk is true
`)}
onLoaded={actionStopLoadingIndicator}
// use delay function so you can animate what you want
delay={() => Math.random() * 1000}
/>
);
此组件支持React Router的生命周期功能,因此您可以处理过渡逻辑。它不依赖任何其他库。
还提供了方便的功能,这些功能可使用react-loadable并在鼠标悬停时预加载页面块。
当浏览器与Webpack空闲时,您甚至可以使用预加载块。
支持页面过渡,您甚至可以控制页面顶部的导航顶部加载指示器。
我遇到了同样的问题,需要更多的灵活性,所以我做到了。
关于您的4个问题:
A)有什么理由不应该将页面之间的转换逻辑存储在每个页面的主要组件内?
是的,当react-router中发生页面更改时,它将卸载当前页面并呈现下一页。
如果您想要过渡,无论如何,您上一页都将被卸载。
B)有什么理由将React-router设计为卸载组件而忽略在不同页面中等待不同类型事件的情况?
是的,因为在这里react-router可以提供一种逻辑,用于处理位置和历史记录以创建webapp。
它做得很好。
C)是否有解决我不考虑的通用方法?
是的,您必须使用withRouter
并使用此API管理过渡。
D)可以从头开始编写自定义反应路由器吗?
如果您认为react-router对您的情况无济于事,那听起来是个好主意。完成后,请在GitHub上分享您的工作。
最佳
答案 1 :(得分:0)
react-route-transition是我制作的一个简单库,可在使用react-router时帮助实现过渡动画。
使用提供的提供程序简单包装您的应用程序,并在组件中使用useTransition
钩子来描述它们的进入和离开动画。
例如,以下操作将在路线更改为“ /”时启动onEnter
动画(在触发前一条路线的onLeave
动画之后)并开始{{1 }}在路径从“ /”更改时动画,紧接在卸载组件(通过更改路径)并触发下一个组件的onLeave
动画之前。
onEnter