我正在使用反应路由器v4和thunk在我的应用程序中进行路由。
我想阻止将<AccountPage />
组件呈现给未登录的用户。我在具有id和令牌的服务器上发送获取请求以检入数据库用户是否具有此令牌。如果有 - 渲染<AccountPage />
,如果没有 - 重定向回家。
我不明白实施“条件路由”的好方法是什么,我找到了几乎完全适合我的任务的东西。
https://gist.github.com/kud/6b722de9238496663031dbacd0412e9d
但问题是condition
中的<RouterIf />
始终未定义,因为fetch是异步的。我试图以异步方式处理这个问题,结果没有任何错误或错误:
Objects are not valid as a React child (found: [object Promise]) ...
或
RouteIf(...): Nothing was returned from render. ...
以下是代码:
//RootComponent
<BrowserRouter>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/terms' component={TermsAndConditionsPage}/>
<Route path='/transaction(\d{13}?)' component={TransactionPage}/>
<RouteIf
condition={( () => {
if( store.getState().userReducer.id, store.getState().userReducer.token) {
// Here i sending id and token on server
// to check in database do user with this id
// has this token
fetch(CHECK_TOKEN_API_URL, {
method: 'post',
headers: {'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({
id: store.getState().userReducer.id,
token: store.getState().userReducer.token
})
})
.then res => {
// If true – <RouteIf /> will render <AccountPage />,
// else - <Redirect to="/">
// But <RouteIf /> mounts without await of this return
// You can see RouteIf file below
if(res.ok) return true
else return false
})
}
})()}
privateRoute={true}
path="/account"
component={AccountPage}
/>
</Switch>
</BrowserRouter>
//RouteIf.js
const RouteIf = ({ condition, privateRoute, path, component }) => {
// The problem is that condition is
// always undefined, because of fetch's asyncronosly
// How to make it wait untill
// <RouteIf condition={...} /> return result?
return condition
? (<PrivateRoute path={path} component={component} />)
:(<Redirect to="/" />)
}
export default RouteIf
如何让condition
等到fetch
回复?或者可能有另一种更好的方法来检查用户是否登录?
答案 0 :(得分:1)
在我的情况下,问题是在执行身份验证检查之前,每次刷新私有页面系统后,用户都会重定向到首页,默认情况下,存储中的令牌值为null,因此默认情况下未授权用户。 我通过将默认Redux状态令牌值更改为undefined来修复它。 在我的情况下,“未定义”表示系统尚未检查用户是否被授权。 如果用户授权的令牌值是某个字符串, 如果未授权-null, 因此PrivateRoute组件看起来
import React from 'react';
import {Redirect, Route} from "react-router-dom";
import {connect} from "react-redux";
const PrivateRoute = ({children, token, ...props}) => {
const renderChildren = () => {
if (!!token) {// if it's a string - show children
return children;
} else if (token === undefined) { // if undefined show nothing, but not redirect
return null; // no need to show even loader, but if necessary, show it here
} else { // else if null show redirect
return (
<Redirect
to={{
pathname: "/",
}}
/>
);
}
};
return (
<Route {...props}>
{renderChildren()}
</Route>
)
};
function mapStateToProps(state) {
return {
token: state.auth.token,
}
}
export default connect(mapStateToProps)(PrivateRoute);
App.js
<Route path="/" exact component={Home}/>
<PrivateRoute path="/profile" component={Profile}/>
答案 1 :(得分:0)
你可以将你的路线包裹在一个有状态的组件中。
然后,在.setAttribute
上检查令牌并将令牌置于状态。
然后在渲染条件中,将路径挂载在state属性上。
componentDidMount
答案 2 :(得分:0)
解决方案是添加第二个标志:gotUnswerFromServer。没有它,组件总是重定向到“/”,而不等待服务器的回答。
export default class PrivateRoute extends React.Component {
constructor(props){
super(props);
this.state = {
isLogged: false,
gotUnswerFromServer: false
}
}
componentDidMount(){
const session = read_cookie('session');
fetch(CHECK_TOKEN_API_URL, {
method: 'post',
headers: {'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ id: session.id, token: session.token })
}).then( res => {
if(res.ok) this.setState({ gotUnswerFromServer: true, isLogged: true })
})
}
render() {
if( this.state.gotUnswerFromServer ){
if( this.state.isLogged ) return <Route path={this.props.path} component={this.props.component}/>
else return <Redirect to={{pathname: '/', state: { from: this.props.location }}} />
} else return null
}
}
答案 3 :(得分:0)
不知道这是否有帮助,但是在搜索了整个Internet之后就做出了这个决定:
https://hackernoon.com/react-authentication-in-depth-part-2-bbf90d42efc9
https://github.com/dabit3/react-authentication-in-depth/blob/master/src/Router.js
import React, { Component } from 'react';
import { Route, Redirect, withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { roleChecker } from '../helpers/formatter';
import { userInfoFetch } from '../api/userInfo';
class PrivateRoute extends Component {
state = {
haveAcces: false,
loaded: false,
}
componentDidMount() {
this.checkAcces();
}
checkAcces = () => {
const { userRole, history } = this.props;
let { haveAcces } = this.state;
// your fetch request
userInfoFetch()
.then(data => {
const { userRoles } = data.data;
haveAcces = roleChecker(userRoles, userRole); // true || false
this.setState({
haveAcces,
loaded: true,
});
})
.catch(() => {
history.push('/');
});
}
render() {
const { component: Component, ...rest } = this.props;
const { loaded, haveAcces } = this.state;
if (!loaded) return null;
return (
<Route
{...rest}
render={props => {
return haveAcces ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: '/',
}}
/>
);
}}
/>
);
}
}
export default withRouter(PrivateRoute);
PrivateRoute.propTypes = {
userRole: PropTypes.string.isRequired,
};
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import PrivateRoute from '../PrivateRoute';
// pages
import Articles from '../../pages/Articles';
import ArticleCreate from '../../pages/ArticleCreate';
const ArticlesRoute = () => {
return (
<Switch>
<PrivateRoute
exact
path="/articles"
userRole="ArticlesEditor"
component={Articles}
/>
<Route
exact
path="/articles/create"
component={ArticleCreate}
/>
</Switch>
);
};
export default ArticlesRoute;
答案 4 :(得分:0)
如果您使用的是redux,则可以显示临时的“正在加载...”视图。仅当用户为空并已加载时,路由才会被重定向。
PrivateRoute.js
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { Route, Redirect } from 'react-router-dom';
import { selectors } from 'settings/reducer';
const PrivateRoute = ({ component: Component, ...rest }) => {
const user = useSelector(state => selectors.user(state));
const isLoaded = useSelector(state => selectors.isLoaded(state));
return (
<Route
{...rest}
render={props =>
!isLoaded ? (
<></>
) : user ? (
<Component {...props} />
) : (
<Redirect to='/sign_in' />
)
}
/>
);
};
export default PrivateRoute;
PrivateRoute.propTypes = {
component: PropTypes.any
};
routes.js
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';
export const Routes = () => (
<BrowserRouter>
<Switch>
<Route exact={true} path='/' component={Home} />
<PrivateRoute path='/account' component={Account} />
</Switch>
</BrowserRouter>
);