React.js应用中SignIn页面无法渲染问题求助
问题:点击SignIn按钮后路由切换但Home页面仍显示,且按钮显示loading
点击导航栏的SignIn按钮后,路由成功切换到http://localhost:3000/signin,该路由已配置为渲染SignIn组件,但Home页面内容仍保持显示,同时Navbar中的SignIn按钮变为“loading...”。SignIn组件采用Google认证,Navbar使用了ReactFire的AuthCheck fallback以及Suspense fallback。
Navbar 代码
import React, { Suspense } from "react"; import { makeStyles } from "@material-ui/core/styles"; import AppBar from "@material-ui/core/AppBar"; import Toolbar from "@material-ui/core/Toolbar"; import Typography from "@material-ui/core/Typography"; import Button from "@material-ui/core/Button"; import Logo from '../assets/images/basketball.png'; import { Link } from 'react-router-dom'; import {AuthCheck} from 'reactfire' const useStyles = makeStyles((theme) => ({ root: { flexGrow: 1, }, menuButton: { marginRight: theme.spacing(2), }, title: { flexGrow: 1, }, logo: { height: "50px", }, link: { color: "black", textDecoration: "none", }, })); export const Navbar = () => { const classes = useStyles(); return ( <div className={classes.root}> <AppBar position="static"> <Toolbar style={{ alignItems: "center", justifyContent: "center", backgroundColor: "rgb(0, 255, 225)" }}> <Link to="/"> <img src={Logo} alt="Logo" className={classes.logo} /> </Link> <Typography style={{flexDirection: 'column', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'black' }} variant="h2" className={classes.title}> NBA Stat Reference </Typography> <Suspense fallback={'loading...'}> <AuthCheck fallback={ <Button style={{color: "black"}}> <Link className={classes.link} to="/signin">Sign In</Link> </Button> }> <Button color="black"> <Link className={classes.link} to="/">Home</Link> </Button> <Button color="black"> <Link className={classes.link} to="/dashboard">Dashboard</Link> </Button> <Button color="black"> <Link className={classes.link} to="/about">About</Link> </Button> </AuthCheck> </Suspense> </Toolbar> </AppBar> </div> ); }
SignIn 组件代码
import React, { useState } from 'react' import firebase from 'firebase/app'; import { AuthCheck, useAuth } from 'reactfire'; import 'firebase/auth'; import { Input } from './Input'; import { Container, Button, makeStyles, Typography, Snackbar, } from '@material-ui/core'; import { withRouter } from "react-router-dom"; import MuiAlert from '@material-ui/lab/Alert'; import { Navbar } from './Navbar' const Alert = (props) => { return <MuiAlert elevation={6} variant="filled" {...props} /> } const useStyles = makeStyles({ googleButton:{ backgroundColor: 'rgb(66,133,244)', marginTop: '2em', padding: '0', color: 'white', height: '50px', width: '240px', border: 'none', textAlign: 'center', boxShadow: 'rgb(0 0 0 / 25%) 0px 2px 4px 0px', fontSize: '16px', lineHeight: '48px', display: 'block', borderRadius: '1px', fontFamily: 'Roboto, arial, sans-serif', cursor: 'pointer' }, googleLogo:{ width: '48px', height: '48px', display: 'block' }, typographyStyle: { fontFamily: 'Roboto, arial, sans-serif;', textAlign: 'center', fontSize: '2em' }, containerStyle: { marginTop: '2em' }, snackBar: { color: 'white', backgroundColor: '#4caf50' } }); export const SignIn = withRouter((props) => { const auth = useAuth(); const classes = useStyles(); const { history } = props; const [open, setOpen] = useState(false); const handleSnackOpen = () => { setOpen(true) }; const handleSnackClose = (event, reason) => { if (reason === "clickaway") { return; } setOpen(false); history.push("/"); }; const sign_in = async () => { const response = await auth.signInWithPopup( new firebase.auth.GoogleAuthProvider()); if(response.user){ handleSnackOpen(); } }; const sign_out = async () => { await auth.signOut(); } return ( <div> <Navbar /> <Container maxWidth = 'sm' className={classes.containerStyle}> <Typography className={classes.typographyStyle}>Sign In Below</Typography> <form> <div> <label htmlFor="email">Email</label> <Input name="email" placeholder="Place Email Here" /> </div> <div> <label htmlFor="password">Password</label> <Input name="password" placeholder="Place Password Here" /> </div> <Button type="submit" variant='contained' color='primary'>Submit</Button> </form> <AuthCheck fallback={ <Button className={classes.googleButton} onClick={sign_in}>Sign In With Google</Button> }> <Button variant='contained' color='secondary' onClick={sign_out}>Sign Out</Button> </AuthCheck> <Snackbar message={'Success'} open={open} autoHideDuration={6000} onClose={handleSnackClose}> <Alert onClose={handleSnackClose} severity="success"> Successful Sign In - Redirect in 6 seconds </Alert> </Snackbar> </Container> </div> ) });
路由配置(index.js)
import React from 'react'; import ReactDOM from 'react-dom'; import {Home} from './client/components/Home'; import {About} from './client/components/About'; import {Dashboard} from './client/components/Dashboard'; import {SignIn} from './client/components/SignIn' import './index.css' import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import {firebaseConfig} from './firebaseConfig'; import {FirebaseAppProvider, AuthCheck} from 'reactfire'; import 'firebase/auth'; import {Provider} from 'react-redux'; import {store} from './client/Redux/store'; import * as ReactDOMClient from 'react-dom/client'; const root = ReactDOMClient.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <FirebaseAppProvider firebaseConfig={firebaseConfig} suspense={true}> <Provider store={store}> <Router> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> <Route path="/signin"> <SignIn /> </Route> </Switch> </Router> </Provider> </FirebaseAppProvider> </React.StrictMode> );
解决方案
1. 解决Home页面仍显示的问题
问题根源是SignIn组件和Home组件各自渲染了Navbar,且Home组件的页面内容可能被放在了Router/Switch的全局区域未做路由隔离。正确处理方式:
- 将Navbar提取为全局组件,放在Switch外部,让所有路由页面共享同一个导航栏。修改index.js结构:
// 修改后的index.js root.render( <React.StrictMode> <FirebaseAppProvider firebaseConfig={firebaseConfig} suspense={true}> <Provider store={store}> <Router> {/* 全局Navbar,所有路由共享 */} <Navbar /> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/dashboard"> <Dashboard /> </Route> <Route path="/signin"> <SignIn /> </Route> </Switch> </Router> </Provider> </FirebaseAppProvider> </React.StrictMode> );
- 删除SignIn组件内部的
<Navbar />代码,避免重复渲染导航栏。
2. 解决SignIn按钮显示loading的问题
Navbar中的Suspense fallback='loading...'会在ReactFire加载Auth状态时触发显示。优化方式:
- 替换fallback为更友好的加载组件,比如Material-UI的CircularProgress:
// Navbar中导入并修改Suspense部分 import CircularProgress from '@material-ui/core/CircularProgress'; // ... <Suspense fallback={<CircularProgress size={24} color="inherit" />}> <AuthCheck fallback={ <Button style={{color: "black"}}> <Link className={classes.link} to="/signin">Sign In</Link> </Button> }> {/* 已认证后的按钮组 */} </AuthCheck> </Suspense>
- 全局预加载Auth状态:在FirebaseAppProvider外层包裹Suspense,让Auth状态在App初始化时完成加载,避免路由切换时重复触发loading:
// 修改后的index.js root.render( <React.StrictMode> <Suspense fallback={<div>加载中...</div>}> <FirebaseAppProvider firebaseConfig={firebaseConfig} suspense={true}> <Provider store={store}> <Router> <Navbar /> <Switch> {/* 路由配置 */} </Switch> </Router> </Provider> </FirebaseAppProvider> </Suspense> </React.StrictMode> );
内容的提问来源于stack exchange,提问作者davezbrown
相关产品推荐
相关产品推荐

