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

React调用useNavigate后URL跳转但目标组件未加载问题求助

问题分析

你的问题核心是登录后调用navigate('/')虽然URL更新,但目标组件未渲染,原登录组件仍显示,根源在于AuthProvider的结构设计导致Router被频繁卸载/重新挂载,同时缺少加载状态处理,引发路由状态异常。

关键问题点:

  1. Router被条件渲染包裹:当前AuthProvider包裹了整个Router,登录后firebaseUser状态变化时,Router会被卸载并重新挂载(从直接渲染Router变为SocketProvider包裹的Router),这会导致react-router的内部状态重置,路由切换逻辑异常。
  2. 缺少加载状态处理:AuthProvider在认证状态尚未确定(loading: true)时就直接渲染内容,可能导致渲染逻辑冲突。
  3. Login组件缺少必要导入:LoginServiceProvider中使用了signInWithEmailAndPassword、signInWithPopup但未导入,可能引发运行时错误。

解决方案

1. 调整Router与AuthProvider的层级

将Router移到AuthProvider外层,确保Router始终稳定挂载,不会因认证状态变化被卸载:

修改index.tsx的根渲染逻辑:

<React.StrictMode>
    <Router>
        <AuthProvider>
            <Navbar/>
            <Routes>
                <Route path="/" element={<App/>} />
                <Route path="/login" element={<Login />} />
                <Route path="/login-service-provider" element={<LoginServiceProvider />} />
                <Route path="/login-client" element={<LoginClient />} />
                <Route path="/signup" element={<SignUp />} />
                <Route path="/signup-service-provider" element={<SignUpServiceProvider />} />
                <Route path="/signup-client" element={<SignUpClient />} />
                <Route path="/profile/serviceProvider/:id" element={<ServiceProviderProfile />} />
                <Route path="/profile/client" element={<ProfileForm />} />
                <Route path="/messages" element={<Messages />} />
                <Route path="/my-orders" element={<MyOrders/>} />
                <Route path="/my-orders/:orderId" element={<MyOrder/>} />
                <Route path="/new-order/:orderName" element={<OrderPage />} />
                <Route path="/error" element={<ErrorPage />} />
                <Route path="*" element={<ErrorPage />} />
            </Routes>
        </AuthProvider>
    </Router>
</React.StrictMode>

2. 完善AuthProvider的加载状态处理

在AuthProvider中先处理加载状态,避免在认证状态未确定时渲染冲突内容:

修改AuthContext.tsx:

import { useState, useEffect, createContext, useContext } from "react";
import { auth } from "../config/firebase";
import { getUserType } from "../service/profile-repository";
import { UserType } from "../constants/UserType";
import { SocketProvider } from "./SocketContext";

export interface CurrentUser {
    firebaseUser: any|null,
    loading: boolean,
    userType: string | null,
}

const AuthContext = createContext<CurrentUser | undefined>(undefined)
export const useAuth = () => {
    const context = useContext(AuthContext);
    if (!context) throw new Error("useAuth must be used within an AuthProvider");
    return context;
};

export const AuthProvider = ({ children } : { children: React.ReactNode }) => {
    const [firebaseUser, setFirebaseUser] = useState<any>(null)
    const [loading, setLoading] = useState<boolean>(true)
    const [userType, setUserType] = useState<string|null>(null)

    useEffect(() => {
        const unsubscribe = auth.onAuthStateChanged(async fbUser => {
            setFirebaseUser(fbUser)
            if(fbUser) {
                const user = await getUserType(fbUser.uid)
                setUserType(user?.userType || null)
            } else {
                setUserType(null)
            }
            setLoading(false)
        })
        return unsubscribe
    }, [])

    const value: CurrentUser = {
        firebaseUser,
        loading,
        userType,
    }

    // 加载中显示提示,防止状态未就绪时的错误渲染
    if (loading) {
        return <div>Loading...</div>;
    }

    return (
        <AuthContext.Provider value={value}>
            {firebaseUser ? (
                <SocketProvider>
                    {children}
                </SocketProvider>
            ) : children}
        </AuthContext.Provider>
    )
}

3. 补全LoginServiceProvider的必要导入

确保导入firebase/auth中的方法,避免运行时错误:

修改LoginServiceProvider.tsx:

import { useState } from "react";
import { auth, googleProvider } from "../../config/firebase";
import { signInWithEmailAndPassword, signInWithPopup, UserCredential } from "firebase/auth";
import "./Login.css";
import { saveUserType } from "../../service/profile-repository";
import { UserType } from "../../constants/UserType";
import { Link, useNavigate } from "react-router-dom";

const LoginServiceProvider = () => {
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const navigate = useNavigate();

    const signIn = async () => {
        try {
            await signInWithEmailAndPassword(auth, email, password);
            navigate('/');
        } catch (error) {
            console.log(error);
        }
    }
    const signUpWithGoogle = async () => {
        try {
            const user: UserCredential = await signInWithPopup(auth, googleProvider);
            await saveUserType(user.user.uid, UserType.SERVICE_PROVIDER)
            navigate('/');
        } catch (error) {
            navigate('/error');
        }
    }

    return (
        <div className="container">
            <div className="header">Service Provider Sign In</div>
            <input
                className="input"
                placeholder="Email.."
                onChange={(e) => setEmail(e.target.value)}
            />
            <input
                type="password"
                className="input"
                placeholder="Password.."
                onChange={(e) => setPassword(e.target.value)}
            />
            <button className="button" onClick={signIn}>Sign In</button>
            <button className="button google-button" onClick={signUpWithGoogle}>Sign In With Google</button>
            <p>Don't have an account? <Link to="/signup">Sign Up</Link></p>
        </div>
    )
}

export default LoginServiceProvider;

4. 可选:添加路由保护(推荐)

为需要登录才能访问的路由(如/)添加认证拦截,避免未登录用户直接访问:

创建一个ProtectedRoute组件:

import { Navigate, Outlet } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";

export const ProtectedRoute = () => {
    const { firebaseUser, loading } = useAuth();

    if (loading) return <div>Loading...</div>;

    return firebaseUser ? <Outlet /> : <Navigate to="/login" />;
};

然后修改路由配置:

<Routes>
    <Route element={<ProtectedRoute />}>
        <Route path="/" element={<App/>} />
        <Route path="/messages" element={<Messages />} />
        <Route path="/my-orders" element={<MyOrders/>} />
    </Route>
    <Route path="/login" element={<Login />} />
    <Route path="/login-service-provider" element={<LoginServiceProvider />} />
    <Route path="/login-client" element={<LoginClient />} />
    <Route path="/signup" element={<SignUp />} />
    <Route path="/signup-service-provider" element={<SignUpServiceProvider />} />
    <Route path="/signup-client" element={<SignUpClient />} />
    <Route path="/profile/serviceProvider/:id" element={<ServiceProviderProfile />} />
    <Route path="/profile/client" element={<ProfileForm />} />
    <Route path="/my-orders/:orderId" element={<MyOrder/>} />
    <Route path="/new-order/:orderName" element={<OrderPage />} />
    <Route path="/error" element={<ErrorPage />} />
    <Route path="*" element={<ErrorPage />} />
</Routes>

内容的提问来源于stack exchange,提问作者Gleb Galkin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 07:25:54