我正在尝试按照此example在React Router v4中创建一个经过身份验证的路由。显示后代的代码:
function PrivateRoute ({component: Component, authed, ...rest}) {
return (
<Route
{...rest}
render={(props) => (!!authed)
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
}
我的身份验证状态(authed
),在reducer中初始化为空对象,是从Redux存储派生的。这就是我的App.js的样子:
class App extends Component {
componentDidMount() {
const token = localStorage.getItem("token");
if (token) {
this.props.fetchUser();
}
}
render() {
return (
<Router>
<div>
<PrivateRoute authed={this.props.authed} path='/dashboard' component={Dashboard} />
/>
</div>
</Router>
);
}
}
问题是authed
状态从未定义开始,然后,一旦挂载路由器组件,它就会将状态更新为true
。然而,这有点晚,因为用户已经被重定向回登录页面。我还尝试用componentDidMount()
替换componentWillMount()
生命周期方法,但这也没有解决问题。
你会建议什么策略?
更新1:解决这个问题的唯一方法是在返回authed
组件之前测试<Route />
状态,如下所示:
render() {
if (!!this.props.authed) {
return (
<Router>
<div>
...
更新2:我正在使用Redux Thunk中间件来发送动作。状态正确地作为道具传递 - 我在console.log()
组件中使用PrivateRoute
方法来验证状态是否正确变异。问题当然是它变异很晚,而Route已经重定向了用户。
粘贴减速器和动作代码......
动作:
export const fetchUser = () => async dispatch => {
dispatch({ type: FETCHING_USER });
try {
const res = await axios.get(`${API_URL}/api/current_user`, {
headers: { authorization: localStorage.getItem("token") }
});
dispatch({ type: FETCH_USER, payload: res.data });
} catch (err) {
// dispatch error action types
}
};
减速机:
const initialState = {
authed: {},
isFetching: false
};
...
case FETCH_USER: // user authenticated
return { ...state, isFetching: false, authed: action.payload };
答案 0 :(得分:1)
我遇到了同样的问题,据我了解,您的更新#1应该是答案。但是,经过进一步调查,我认为这是一个体系结构问题。您的专用路由的当前实现方式取决于同步信息。
如果我们务实地考虑问题,ProtectedRoute
本质上会根据应用程序的状态返回重定向或组件。除了使用组件包装每个Route
之外,我们还可以将所有路线包装在一个组件中并从商店中提取信息。
是的,为受保护的路由编写更多的代码,您需要测试这是否可行。
编辑:忘记提及这是一个体系结构问题的另一个重要原因是,如果用户刷新受保护的页面,他们将被重定向。
更新 更好的解决方案: 刷新后,如果通过身份验证,它将重定向到其目标uri https://tylermcginnis.com/react-router-protected-routes-authentication/
解决方案1
//You can make this a method instead, that way we don't need to pass authed as an argument
function Protected(authed, Component, props) {
return !!authed
? <Component {...props} />
: <Redirect to='/login' />
}
class AppRouter extends React.PureComponent {
componentDidMount() {
const token = localStorage.getItem("token");
if (token) {
this.props.fetchUser();
}
}
render() {
let authed = this.props.authed
return (
<Router>
<Route path='/protected' render={(props) => Protected(authed, Component, props)} />
</Router>
)
}
}
class App extends Component {
render() {
return (
<Provider store={store}>
<AppRouter />
</Provider>
)
}
}
解决方案2 或者我们可以只检查每个组件(是的,很痛苦)
class Component extends React.Component {
render() {
return (
!!this.props.authed
? <div>...</div>
: <Redirect to='/' />
)
}
}
答案 1 :(得分:0)
如果您的App
组件已连接到redux商店,则可能表示this.props.authed
而不是this.state.authed
我的身份验证状态(authed),初始化为空 reducer上的对象派生自Redux商店
所以你在这里将空对象与true
进行比较:(props) => authed === true
?为什么不用false
初始化它?
您确定行动this.props.fetchUser
正在将状态切换为true
吗?
也许你最好发布你的动作和减速器文件
答案 2 :(得分:0)
理论上,您需要从NODE API调用中获得一个您现在没有获得的承诺。您需要进行体系结构更改。我建议你使用redux-promise-middleware这是一个redux中间件。我的github帐户中有一个示例项目。如果您对this.props.fetchUser()的调用已完成,您将收到通知,根据使用Promise的调用,您可以处理此异步问题。如果需要帮助,请转到那个回购。
答案 3 :(得分:0)
我也遇到了同样的问题,我正在使用临时技巧来解决此问题,方法是将加密的值存储在localStorage
内,然后将其解密到我的PrivateRoute
组件中,然后检查该值是否匹配。
action.js
localStorage.setItem('isAuthenticated', encryptedText);
PrivateRoute.js
if (localStorage.getItem('isAuthenticated')) {
const isAuth = decryptedText === my_value;
return (
<Route
{...rest}
render={(props) =>
isAuth ? <Component {...props} /> : <Redirect to="/login" />
}
/>
);
} else {
return <Redirect to="/login" />;
}
由于localStorage
速度更快,因此不必进行不必要的重定向。如果有人删除localStorage
,他们将被直接重定向到/login
注意:这是一个临时解决方案。