在 useEffect 钩子完成之前 ReactJS 认证路由组件渲染

时间:2021-03-27 11:27:29

标签: javascript reactjs react-router react-hooks react-context

我正在尝试使用 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;

1 个答案:

答案 0 :(得分:2)

我无法重现您的确切问题,但我认为您的 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>
)