Javascript 使用React和Firebase时,受保护的路线无法正常工作

Javascript 使用React和Firebase时,受保护的路线无法正常工作,javascript,reactjs,firebase,Javascript,Reactjs,Firebase,我正在用firebase和react构建一个小应用程序,目前正在实施身份验证。我已将我的应用程序组件中的onAuthStateChanged设置为副作用,每当用户登录时,它都应该从ProtectedRoute重定向到所需的组件 这可以正常工作,但不幸的是,在刷新页面时,ProtectedRoute没有呈现正确的组件,只是触发了重定向 我知道发生了什么:在刷新时,用户是空的,只有在那个之后它才会改变,所以我希望看到屏幕闪烁和正确的重定向 你能看一下下面的代码,告诉我如何修复这个行为吗 应用程序组件

我正在用firebase和react构建一个小应用程序,目前正在实施身份验证。我已将我的应用程序组件中的onAuthStateChanged设置为副作用,每当用户登录时,它都应该从ProtectedRoute重定向到所需的组件

这可以正常工作,但不幸的是,在刷新页面时,ProtectedRoute没有呈现正确的组件,只是触发了重定向

我知道发生了什么:在刷新时,用户是空的,只有在那个之后它才会改变,所以我希望看到屏幕闪烁和正确的重定向

你能看一下下面的代码,告诉我如何修复这个行为吗

应用程序组件:

const App = () => {
  const [authUser, setAuthUser] = useState<firebase.User | null>(null);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      authUser ? setAuthUser(authUser) : setAuthUser(null);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />

          <Route exact path={ROUTES.LANDING} component={Landing} />
          <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
          <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
          <Route
            exact
            path={ROUTES.PASSWORD_FORGET}
            component={PasswordForget}
          />
          <ProtectedRoute exact path={ROUTES.HOME} component={Home} />
          <ProtectedRoute exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ADMIN} component={Admin} />
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const App = () => {
  const [authUser, setAuthUser] = useState(false);
  const [authPending, setAuthPending] = useState(true);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      setAuthUser(!!authUser);
      setAuthPending(false);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />
          <Switch>
            <Route exact path={ROUTES.LANDING} component={Landing} />
            <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
            <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
            <Route
              exact
              path={ROUTES.PASSWORD_FORGET}
              component={PasswordForget}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.HOME}
              component={Home}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.ACCOUNT}
              component={Account}
            />
            <Route exact path={ROUTES.ACCOUNT} component={Account} />
            <Route exact path={ROUTES.ADMIN} component={Admin} />
          </Switch>
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
  pendingAuth: boolean;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  pendingAuth,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  if (pendingAuth) {
    return <div>Authenticating</div>;
  }

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const-App=()=>{
const[authUser,setAuthUser]=useState(null);
const Firebase=useContext(FirebaseContext);
useffect(()=>{
const authListener=Firebase!.auth.onAuthStateChanged((authUser)=>{
authUser?setAuthUser(authUser):setAuthUser(null);
});
return()=>authListener();
//eslint禁用下一行react HOOK/deps
}, []);
返回(

); };
受保护的路由:

const App = () => {
  const [authUser, setAuthUser] = useState<firebase.User | null>(null);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      authUser ? setAuthUser(authUser) : setAuthUser(null);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />

          <Route exact path={ROUTES.LANDING} component={Landing} />
          <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
          <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
          <Route
            exact
            path={ROUTES.PASSWORD_FORGET}
            component={PasswordForget}
          />
          <ProtectedRoute exact path={ROUTES.HOME} component={Home} />
          <ProtectedRoute exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ADMIN} component={Admin} />
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const App = () => {
  const [authUser, setAuthUser] = useState(false);
  const [authPending, setAuthPending] = useState(true);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      setAuthUser(!!authUser);
      setAuthPending(false);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />
          <Switch>
            <Route exact path={ROUTES.LANDING} component={Landing} />
            <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
            <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
            <Route
              exact
              path={ROUTES.PASSWORD_FORGET}
              component={PasswordForget}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.HOME}
              component={Home}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.ACCOUNT}
              component={Account}
            />
            <Route exact path={ROUTES.ACCOUNT} component={Account} />
            <Route exact path={ROUTES.ADMIN} component={Admin} />
          </Switch>
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
  pendingAuth: boolean;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  pendingAuth,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  if (pendingAuth) {
    return <div>Authenticating</div>;
  }

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
接口道具扩展了RouteProps{
组件?:任何;
儿童?:任何;
}
const ProtectedRoute:React.FC=({
组件:组件,
儿童
休息
}) => {
const authUser=useContext(AuthUserContext);
返回(
!!授权用户(
组件(
) : (
儿童
)
) : (
)
}
/>
);
};

找到了修复程序。Had必须添加检查用户身份验证状态的标志(该标志的默认值设置为true)。需要将标志作为prop传递给ProtectedRoute,如果为True,则呈现一些加载组件:

应用程序组件:

const App = () => {
  const [authUser, setAuthUser] = useState<firebase.User | null>(null);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      authUser ? setAuthUser(authUser) : setAuthUser(null);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />

          <Route exact path={ROUTES.LANDING} component={Landing} />
          <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
          <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
          <Route
            exact
            path={ROUTES.PASSWORD_FORGET}
            component={PasswordForget}
          />
          <ProtectedRoute exact path={ROUTES.HOME} component={Home} />
          <ProtectedRoute exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ADMIN} component={Admin} />
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const App = () => {
  const [authUser, setAuthUser] = useState(false);
  const [authPending, setAuthPending] = useState(true);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      setAuthUser(!!authUser);
      setAuthPending(false);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />
          <Switch>
            <Route exact path={ROUTES.LANDING} component={Landing} />
            <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
            <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
            <Route
              exact
              path={ROUTES.PASSWORD_FORGET}
              component={PasswordForget}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.HOME}
              component={Home}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.ACCOUNT}
              component={Account}
            />
            <Route exact path={ROUTES.ACCOUNT} component={Account} />
            <Route exact path={ROUTES.ADMIN} component={Admin} />
          </Switch>
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
  pendingAuth: boolean;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  pendingAuth,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  if (pendingAuth) {
    return <div>Authenticating</div>;
  }

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const-App=()=>{
const[authUser,setAuthUser]=useState(false);
const[authPending,setAuthPending]=useState(true);
const Firebase=useContext(FirebaseContext);
useffect(()=>{
const authListener=Firebase!.auth.onAuthStateChanged((authUser)=>{
setAuthUser(!!authUser);
setAuthPending(false);
});
return()=>authListener();
//eslint禁用下一行react HOOK/deps
}, []);
返回(

); };
受保护的路由:

const App = () => {
  const [authUser, setAuthUser] = useState<firebase.User | null>(null);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      authUser ? setAuthUser(authUser) : setAuthUser(null);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />

          <Route exact path={ROUTES.LANDING} component={Landing} />
          <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
          <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
          <Route
            exact
            path={ROUTES.PASSWORD_FORGET}
            component={PasswordForget}
          />
          <ProtectedRoute exact path={ROUTES.HOME} component={Home} />
          <ProtectedRoute exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ACCOUNT} component={Account} />
          <Route exact path={ROUTES.ADMIN} component={Admin} />
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
const App = () => {
  const [authUser, setAuthUser] = useState(false);
  const [authPending, setAuthPending] = useState(true);
  const Firebase = useContext(FirebaseContext);

  useEffect(() => {
    const authListener = Firebase!.auth.onAuthStateChanged((authUser) => {
      setAuthUser(!!authUser);
      setAuthPending(false);
    });

    return () => authListener();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <AuthUserContext.Provider value={authUser}>
      <Router>
        <div>
          <Navigation />

          <hr />
          <Switch>
            <Route exact path={ROUTES.LANDING} component={Landing} />
            <Route exact path={ROUTES.SIGN_UP} component={SignUpPage} />
            <Route exact path={ROUTES.SIGN_IN} component={SignIn} />
            <Route
              exact
              path={ROUTES.PASSWORD_FORGET}
              component={PasswordForget}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.HOME}
              component={Home}
            />
            <ProtectedRoute
              pendingAuth={authPending}
              exact
              path={ROUTES.ACCOUNT}
              component={Account}
            />
            <Route exact path={ROUTES.ACCOUNT} component={Account} />
            <Route exact path={ROUTES.ADMIN} component={Admin} />
          </Switch>
        </div>
      </Router>
    </AuthUserContext.Provider>
  );
};
interface Props extends RouteProps {
  component?: any;
  children?: any;
  pendingAuth: boolean;
}

const ProtectedRoute: React.FC<Props> = ({
  component: Component,
  children,
  pendingAuth,
  ...rest
}) => {
  const authUser = useContext(AuthUserContext);

  if (pendingAuth) {
    return <div>Authenticating</div>;
  }

  return (
    <Route
      {...rest}
      render={(routeProps) =>
        !!authUser ? (
          Component ? (
            <Component {...routeProps} />
          ) : (
            children
          )
        ) : (
          <Redirect
            to={{
              pathname: ROUTES.SIGN_IN,
              state: { from: routeProps.location },
            }}
          />
        )
      }
    />
  );
};
接口道具扩展了RouteProps{
组件?:任何;
儿童?:任何;
pendingAuth:布尔型;
}
const ProtectedRoute:React.FC=({
组件:组件,
儿童
彭丁格斯,
休息
}) => {
const authUser=useContext(AuthUserContext);
if(pendingAuth){
返回认证;
}
返回(
!!授权用户(
组件(
) : (
儿童
)
) : (
)
}
/>
);
};

不是一个解决方案,但是:我认为返回组件的顶级条件有时会产生奇怪的行为?我会尝试重写为简单返回的条件语句块。例如,
if(authUser&&Component)return
等将有助于查明问题。感谢您的帮助,我想我已经找到了解决方案