useEffect hook 完成前的 ReactJS 认证路由组件渲染
ReactJS authentication routing component rendering before useEffect hook completes
我正在尝试使用 Firebase 身份验证实现受保护的页面。我创建了一个典型的 PrivateRoute 组件,它应该只在用户登录时显示页面,或者在用户未登录时将用户重定向到登录页面。
我在 App.js 中使用 useEffect 挂钩将身份验证状态存储在全局状态中。经过大量阅读和研究,我了解到 useEffect 仅在 Child 组件加载后完成。
话虽如此,我对如何将经过身份验证的 App 传递给 PrivateRoute 感到茫然。使用我当前的代码,经过身份验证的状态仅在 PrivateRoute 将用户推送到登录页面后才注册为 true。非常感谢任何帮助。
App.js
//context
const [{user, authenticated}, dispatch] = useStateValue();
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
console.log("THE USER IS >>> ", authUser);
if (authUser) {
dispatch({
type: "SET_USER",
user: authUser,
});
dispatch({
type: "SET_AUTH",
authenticated: true,
})
} else {
// the user is logged out
dispatch({
type: "SET_USER",
user: null,
});
dispatch({
type: "SET_AUTH",
authenticated: false,
})
}
});
}, []);
return (
<Router>
<div className="app">
<PrivateRoute exact isAuth={authenticated} path="/account/create-store" component={CreateAccount} />
</div>
</Router>
)
PrivateRoute.js
import { Route, Redirect } from 'react-router';
import { useStateValue } from './StateProvider';
function PrivateRoute ({ isAuth: isAuth, component: Component, ...rest }) {
const [{authenticated}, dispatch] = useStateValue();
return (
<Route {...rest} render={(props) => {
if (isAuth) {
return <Component />
} else {
return (
<Redirect to={{ pathname:"/login", state: {from: props.location }}} />
);
}
}} />
)
}
export default PrivateRoute
reducer.js
export const initialState = {
user: null,
authenticated: false,
};
const reducer = (state, action) => {
console.log(action)
switch(action.type) {
case "SET_USER":
return {
...state,
user: action.user,
}
case "SET_AUTH":
return {
...state,
authenticated: action.authenticated,
}
default:
return state;
}}
export default reducer;
我无法重现您的确切问题,但我认为您的 PrivateRoute 是错误的。试试下面的例子。
function PrivateRoute({ isAuth, component: Component, ...rest }) {
if (!isAuth) {
return <Redirect to={{ pathname:"/login", state: {from: props.location }}} />;
}
return <Route component={Component} {...rest} />;
}
export default PrivateRoute;
使用 isLoading 状态变量,这样您就不会在检查 firebase.auth().onAuthStateChanged 之前被重定向。
const [{user, authenticated}, dispatch] = useStateValue();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
console.log("THE USER IS >>> ", authUser);
if (authUser) {
dispatch({
type: "SET_USER",
user: authUser,
});
dispatch({
type: "SET_AUTH",
authenticated: true,
})
} else {
// the user is logged out
dispatch({
type: "SET_USER",
user: null,
});
dispatch({
type: "SET_AUTH",
authenticated: false,
})
}
setIsLoading(false);
});
}, []);
if(isLoading) {
return <div>Loading...</div>
}
return (
<Router>
<div className="app">
<PrivateRoute exact isAuth={authenticated} path="/account/create-store" component={CreateAccount} />
</div>
</Router>
)
我正在尝试使用 Firebase 身份验证实现受保护的页面。我创建了一个典型的 PrivateRoute 组件,它应该只在用户登录时显示页面,或者在用户未登录时将用户重定向到登录页面。
我在 App.js 中使用 useEffect 挂钩将身份验证状态存储在全局状态中。经过大量阅读和研究,我了解到 useEffect 仅在 Child 组件加载后完成。
话虽如此,我对如何将经过身份验证的 App 传递给 PrivateRoute 感到茫然。使用我当前的代码,经过身份验证的状态仅在 PrivateRoute 将用户推送到登录页面后才注册为 true。非常感谢任何帮助。
App.js
//context
const [{user, authenticated}, dispatch] = useStateValue();
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
console.log("THE USER IS >>> ", authUser);
if (authUser) {
dispatch({
type: "SET_USER",
user: authUser,
});
dispatch({
type: "SET_AUTH",
authenticated: true,
})
} else {
// the user is logged out
dispatch({
type: "SET_USER",
user: null,
});
dispatch({
type: "SET_AUTH",
authenticated: false,
})
}
});
}, []);
return (
<Router>
<div className="app">
<PrivateRoute exact isAuth={authenticated} path="/account/create-store" component={CreateAccount} />
</div>
</Router>
)
PrivateRoute.js
import { Route, Redirect } from 'react-router';
import { useStateValue } from './StateProvider';
function PrivateRoute ({ isAuth: isAuth, component: Component, ...rest }) {
const [{authenticated}, dispatch] = useStateValue();
return (
<Route {...rest} render={(props) => {
if (isAuth) {
return <Component />
} else {
return (
<Redirect to={{ pathname:"/login", state: {from: props.location }}} />
);
}
}} />
)
}
export default PrivateRoute
reducer.js
export const initialState = {
user: null,
authenticated: false,
};
const reducer = (state, action) => {
console.log(action)
switch(action.type) {
case "SET_USER":
return {
...state,
user: action.user,
}
case "SET_AUTH":
return {
...state,
authenticated: action.authenticated,
}
default:
return state;
}}
export default reducer;
我无法重现您的确切问题,但我认为您的 PrivateRoute 是错误的。试试下面的例子。
function PrivateRoute({ isAuth, component: Component, ...rest }) {
if (!isAuth) {
return <Redirect to={{ pathname:"/login", state: {from: props.location }}} />;
}
return <Route component={Component} {...rest} />;
}
export default PrivateRoute;
使用 isLoading 状态变量,这样您就不会在检查 firebase.auth().onAuthStateChanged 之前被重定向。
const [{user, authenticated}, dispatch] = useStateValue();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
console.log("THE USER IS >>> ", authUser);
if (authUser) {
dispatch({
type: "SET_USER",
user: authUser,
});
dispatch({
type: "SET_AUTH",
authenticated: true,
})
} else {
// the user is logged out
dispatch({
type: "SET_USER",
user: null,
});
dispatch({
type: "SET_AUTH",
authenticated: false,
})
}
setIsLoading(false);
});
}, []);
if(isLoading) {
return <div>Loading...</div>
}
return (
<Router>
<div className="app">
<PrivateRoute exact isAuth={authenticated} path="/account/create-store" component={CreateAccount} />
</div>
</Router>
)