You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React.js应用中SignIn页面无法渲染问题求助

问题:点击SignIn按钮后路由切换但Home页面仍显示,且按钮显示loading

点击导航栏的SignIn按钮后,路由成功切换到http://localhost:3000/signin,该路由已配置为渲染SignIn组件,但Home页面内容仍保持显示,同时Navbar中的SignIn按钮变为“loading...”。SignIn组件采用Google认证,Navbar使用了ReactFire的AuthCheck fallback以及Suspense fallback。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:45:29