我有一个像这样的初始redux状态:
{
loggedInUserId: null,
comments: []
}
以下是我的React App的样子:
class App extends Component {
componentWillMount() {
this.props.getLoggedInUserId();
}
render() {
return (
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/comments" component={Comments} />
</Switch>
);
}
}
在我的应用程序中,我发送了一个动作getLoggedInUserId()
,它异步填充州内的loggedInUserId
。
HomePage是一个显示一些文本的愚蠢组件。我启动应用程序(路由现在是'/'),看到HomePage组件,然后我导航到评论页面,其中包含:
componentWillMount() {
this.props.fetchComments(this.props.loggedInUserId); // Dispatch action to do API call to fetch user's comments
}
render() {
// Show this.props.comments nicely formatted
}
一切正常,我在评论组件中看到了评论列表。
但是如果我刷新路径/comments
上的页面,那么当Comment运行componentWillMount时,尚未加载loggedInUserId,因此它将调用fetchComments(null)
。
现在,为了解决这个问题,我正在使用我的Comments组件:
componentWillMount() {
if (!this.props.loggedInUserId) return;
this.props.fetchComments(this.props.loggedInUserId);
}
componentWillReceiveProps(nextProps) {
if (!this.props.loggedInUserId && nextProps.loggedInUserId) {
nextProps.fetchComments(nextProps.loggedInUserId);
}
}
效果很好。但我在10多个组件中这样做,似乎很多工作可以分解,但我没有找到一种优雅的方法来做到这一点。
所以我问你一般如何应对这种情况?欢迎任何想法:
我正在使用Route
的包装器,它检查用户是否已登录,如果没有,则将它们重定向到登录页面。只有在获取经过身份验证的用户的userId后才会呈现已包装的路由。
import * as React from 'react'
import { Route, Redirect } from 'react-router-dom'
import URLSearchParams from 'url-search-params'
class AuthRoute extends React.Component {
componentDidMount() {
if (!this.props.isLoading) {
this.props.getLoggedInUserId()
}
}
render() {
if (this.props.isLoading) {
// first request is fired to fetch authenticated user
return null // or spinner
} else if (this.props.isAuthenticated) {
// user is authenticated
return <Route {...this.props} />
} else {
// invalid user or authentication expired
// redirect to login page and remember original location
const search = new URLSearchParams({
next: this.props.location.pathname,
})
const next =
this.props.location.pathname !== '/' ? `?${search.toString()}` : ''
return <Redirect to={`/login${next}`} />
}
}
}
您需要更新处理getLoggedInUserId
动作的reducer以存储isLoading
状态。
您可能希望服务器将初始状态呈现为“index.html”(或者您拥有的)并在客户端上进行水合。
这个初始状态包括loggedInUserId
和/comments
页面的数据。
我认为在这里使用HOC会很干净。因为所有常见的逻辑将在同一个地方。在这里使用组合假设你有组件A,B,C,D
现在,您想在所有组件的componentWillReceiveProps生命周期中编写一些常用函数。
写一个HOC,如:
class HOC extends React.Component {
componentWillReceiveProps(nextProps) {
//Your commomn logic
}
render() {
const childrenWithProps = React.Children.map(this.props.children,
child => React.cloneElement(child, {
...this.props,
})
return (
<div>
{childrenWithProps}
</div>
)
}
}
像这样写你的组件:
class A extends React.Component {
componentWillReceiveProps(nextProps) {
//your uncommone logic
}
render(){
return (
<HOC {...this.props}>
<div>
//Your page jsx
</div>
</HOC>
)
}
}
以相同的方式为组件B,C和D编写。当组件之间存在许多共同点时,此模式非常有用。所以最好看看你的用例
OP写作。在阅读好的想法之后,我决定选择自定义的HOC:
import React, { Component } from 'react';
const requireProp = (As, propsSelector, propsToDispatch) =>
class Wrapper extends Component {
componentWillMount() {
if (!propsSelector(this.props) && typeof propsToDispatch === 'function') {
propsToDispatch(this.props);
}
}
render() {
const { ...props } = this.props;
return !!propsSelector(this.props) && <As {...props} />;
}
};
export default requireProp;
要了解我如何使用它,请参阅this gist。