简体   繁体   中英

ReactJS authentication routing component rendering before useEffect hook completes

I'm trying to implement protected pages with Firebase authentication. I created a typical PrivateRoute component that is supposed to only show the page if they're logged in, or redirect users to a login page if they aren't logged in.

I stored the authentication status in a global state using a useEffect hook in App.js. After a lot of reading and research, I understand that the useEffect only completes after the Child component has loaded.

Having said that, I'm at a loss on how to pass authenticated from App to PrivateRoute. With my current code, the authenticated state only registers as true after PrivateRoute has pushed users to the login page. Would appreciate any help.

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;

I can't reproduce your exact problem but I think your PrivateRoute is wrong. Try something like the example bellow.

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;

Use a isLoading state variable so you are not redirected before checking the 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>
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM