使用 firebase auth 刷新受保护的路由 React Router
Refresh on protected Routes React Router with firebase auth
我正在使用 firebase、react 和 redux 构建一个待办事项网络应用程序。所以我已经保护了诸如 "/todos/add"
、"/dashboard"
和访客路由 "/"
、"/signup"
、"/login"
这样的路由,如果你是已通过身份验证。
我的问题是,当我在 "/todos/add"
路由中进行身份验证时,我重新加载应用程序重新加载的页面,并且我的身份验证操作被调度,将我的用户放入 redux 商店,但我被重定向到 "/login"
然后到"/dashboard"
。但我想在重新加载之前在同一页面上。我的代码:
PrivateRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const PrivateRoute = ({
isAuthenticated,
isLoading,
component: Component,
...rest
}) => (
<Route
{...rest}
render={props =>
isAuthenticated ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: state.user.isLoading
});
PrivateRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(PrivateRoute);
GuestRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const GuestRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
!isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to="/dashboard"
/>
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: !!state.user.isLoading
});
GuestRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(GuestRoute);
App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
// COMPONENTS
import HomePage from "./components/pages/homepage/HomePage";
import SignupPage from "./components/pages/signuppage/SignupPage";
import LoginPage from "./components/pages/loginpage/LoginPage";
import DashboardPage from "./components/pages/dashboardpage/DashboardPage";
// HOC
import Layout from "./components/hoc/layout/Layout";
// ROUTES
import PrivateRoute from "./routes/PrivateRoute";
import GuestRoute from "./routes/GuestRoute";
import AddTodosPage from "./components/pages/todos/add/AddTodosPage";
const App = () => (
<Layout>
<Switch>
<Route path="/" exact component={HomePage} />
<GuestRoute path="/signup" exact component={SignupPage} />
<GuestRoute path="/login" exact component={LoginPage} />
<PrivateRoute path="/todos/add" exact component={AddTodosPage} />
<PrivateRoute path="/dashboard" exact component={DashboardPage} />
</Switch>
</Layout>
);
export default App;
FIX
将 isLoading
默认设置为 true
,这将是刷新时的值。 (在 firebase 身份验证后将 isLoading
设置为 false
。)并且 <PrivateRoute>
中的条件应为 (isLoading || isAuthenticated )
。
但重要: 这里 isLoading
和 isAuthenticated
应该在 firebase 身份验证响应后一起设置。或者 isAuthenticated
应在 isLoading
设置为 false
之前相应设置。
说明
一旦您在 /todos/add
的 <PrivateRoute>
中刷新,它就会查找 isAuthenticated
值。这里似乎是 false
,因此将被重定向到 /login
路线。
当处于 /login
的 <GuestRoute>
时,它将再次查看 !isAuthenticated
值。此时 isAuthenticated
值似乎是 true
使其重定向回 /dashboard
.
所以从这个观察可以看出, isAuthenticated
值并不是一刷新就设置为 true
。而是这个 firebase 身份验证请求是异步操作,其中会有一些值得注意的响应。但在响应之前,重定向会以默认 isAuthenticated
值或 undefined
作为值。 In-between 此 re-directions,将完成 firebase 身份验证调用并将 isAuthenticated
设置为 true
以最终重定向到 /dashboard
.
但是这里使用 isLoading
值来延迟重定向。默认情况下将 isLoading
设置为 true
,这将是刷新时的值。 (在 firebase 身份验证后将 isLoading
设置为 false
。)并且 <PrivateRoute>
中的条件应为 (isLoading || isAuthenticated )
。 重要提示: 这里 isLoading
和 isAuthenticated
应该在 firebase 身份验证响应后一起设置。或 isAuthenticated
应在 isLoading
设置为 false
之前相应设置。 (否则如果 isLoading
设置为 false
并且如果 isAuthenticated
仍然是 false
或 undefined
它将被重定向!)
不要渲染您的路线,而是尝试渲染类似 'a loading state' 的内容。
否则,如果身份验证调用需要一些时间来解决,您最终将向未经身份验证的用户显示您的私有路由。
const PrivateRoute = ({
component: Component,
permission: Permission,
waitingForAuth: Waiting,
...rest
}) => (
<Route
{...rest}
render={props => {
if (Waiting) return <LinearProgress />;
return Permission ? <Component {...props} /> : <Redirect to="/" />;
}}
/>
);
我的堆栈(firebase + vuex + vuejs)也遇到了类似的问题。我的解决方案是在我的商店中添加一个 userLoading
状态,即 Promise<void>;
.
我的设计是将 auth 实例传递给我的存储状态构造函数,并在 auth 状态更改时解析 userLoading
promise。
我的路由使用 await
作为承诺,然后检查是否有用户。
这是我的状态构造函数:
public constructor(auth: firebase.auth.Auth) {
this.user = auth.currentUser;
this.userLoading = new Promise(resolve => {
auth.onAuthStateChanged(async (user: firebase.User | null) => {
if (user) {
await user!.getIdToken(true);
const decodedToken = await user!.getIdTokenResult();
this.stripeRole = decodedToken.claims.stripeRole;
}
this.user = user;
resolve();
});
});
}
我正在使用 firebase、react 和 redux 构建一个待办事项网络应用程序。所以我已经保护了诸如 "/todos/add"
、"/dashboard"
和访客路由 "/"
、"/signup"
、"/login"
这样的路由,如果你是已通过身份验证。
我的问题是,当我在 "/todos/add"
路由中进行身份验证时,我重新加载应用程序重新加载的页面,并且我的身份验证操作被调度,将我的用户放入 redux 商店,但我被重定向到 "/login"
然后到"/dashboard"
。但我想在重新加载之前在同一页面上。我的代码:
PrivateRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const PrivateRoute = ({
isAuthenticated,
isLoading,
component: Component,
...rest
}) => (
<Route
{...rest}
render={props =>
isAuthenticated ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: state.user.isLoading
});
PrivateRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(PrivateRoute);
GuestRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const GuestRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
!isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to="/dashboard"
/>
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: !!state.user.isLoading
});
GuestRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(GuestRoute);
App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
// COMPONENTS
import HomePage from "./components/pages/homepage/HomePage";
import SignupPage from "./components/pages/signuppage/SignupPage";
import LoginPage from "./components/pages/loginpage/LoginPage";
import DashboardPage from "./components/pages/dashboardpage/DashboardPage";
// HOC
import Layout from "./components/hoc/layout/Layout";
// ROUTES
import PrivateRoute from "./routes/PrivateRoute";
import GuestRoute from "./routes/GuestRoute";
import AddTodosPage from "./components/pages/todos/add/AddTodosPage";
const App = () => (
<Layout>
<Switch>
<Route path="/" exact component={HomePage} />
<GuestRoute path="/signup" exact component={SignupPage} />
<GuestRoute path="/login" exact component={LoginPage} />
<PrivateRoute path="/todos/add" exact component={AddTodosPage} />
<PrivateRoute path="/dashboard" exact component={DashboardPage} />
</Switch>
</Layout>
);
export default App;
FIX
将 isLoading
默认设置为 true
,这将是刷新时的值。 (在 firebase 身份验证后将 isLoading
设置为 false
。)并且 <PrivateRoute>
中的条件应为 (isLoading || isAuthenticated )
。
但重要: 这里 isLoading
和 isAuthenticated
应该在 firebase 身份验证响应后一起设置。或者 isAuthenticated
应在 isLoading
设置为 false
之前相应设置。
说明
一旦您在 /todos/add
的 <PrivateRoute>
中刷新,它就会查找 isAuthenticated
值。这里似乎是 false
,因此将被重定向到 /login
路线。
当处于 /login
的 <GuestRoute>
时,它将再次查看 !isAuthenticated
值。此时 isAuthenticated
值似乎是 true
使其重定向回 /dashboard
.
所以从这个观察可以看出, isAuthenticated
值并不是一刷新就设置为 true
。而是这个 firebase 身份验证请求是异步操作,其中会有一些值得注意的响应。但在响应之前,重定向会以默认 isAuthenticated
值或 undefined
作为值。 In-between 此 re-directions,将完成 firebase 身份验证调用并将 isAuthenticated
设置为 true
以最终重定向到 /dashboard
.
但是这里使用 isLoading
值来延迟重定向。默认情况下将 isLoading
设置为 true
,这将是刷新时的值。 (在 firebase 身份验证后将 isLoading
设置为 false
。)并且 <PrivateRoute>
中的条件应为 (isLoading || isAuthenticated )
。 重要提示: 这里 isLoading
和 isAuthenticated
应该在 firebase 身份验证响应后一起设置。或 isAuthenticated
应在 isLoading
设置为 false
之前相应设置。 (否则如果 isLoading
设置为 false
并且如果 isAuthenticated
仍然是 false
或 undefined
它将被重定向!)
不要渲染您的路线,而是尝试渲染类似 'a loading state' 的内容。 否则,如果身份验证调用需要一些时间来解决,您最终将向未经身份验证的用户显示您的私有路由。
const PrivateRoute = ({
component: Component,
permission: Permission,
waitingForAuth: Waiting,
...rest
}) => (
<Route
{...rest}
render={props => {
if (Waiting) return <LinearProgress />;
return Permission ? <Component {...props} /> : <Redirect to="/" />;
}}
/>
);
我的堆栈(firebase + vuex + vuejs)也遇到了类似的问题。我的解决方案是在我的商店中添加一个 userLoading
状态,即 Promise<void>;
.
我的设计是将 auth 实例传递给我的存储状态构造函数,并在 auth 状态更改时解析 userLoading
promise。
我的路由使用 await
作为承诺,然后检查是否有用户。
这是我的状态构造函数:
public constructor(auth: firebase.auth.Auth) {
this.user = auth.currentUser;
this.userLoading = new Promise(resolve => {
auth.onAuthStateChanged(async (user: firebase.User | null) => {
if (user) {
await user!.getIdToken(true);
const decodedToken = await user!.getIdTokenResult();
this.stripeRole = decodedToken.claims.stripeRole;
}
this.user = user;
resolve();
});
});
}