firebase 为什么这个重定向无法在我的React 18应用程序中加载正确的组件?

avwztpqn  于 12个月前  发布在  React
关注(0)|答案(1)|浏览(70)

我正在用React 18和Firebase 9做一个聊天应用。
我遇到了一个问题,而在成功注册后重定向用户的工作。
App.js中,我有:

import { useContext } from 'react';
import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
  RouterProvider,
  Navigate
} from 'react-router-dom';
import RootLayout from './layouts/RootLayout';
import AuthLayout from './layouts/AuthLayout';
import Chat from './pages/Chat';
import Register from './pages/Register';
import Login from './pages/Login';
import { AuthContext } from './contexts/AuthContext';

export default function App() {
  const { currentUser } = useContext(AuthContext);

  const ProtectedRoute = ({ children }) => {
    if (!currentUser) {
      return <Navigate to="/auth/login" />;
    }
    return children;
  };

  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" element={<RootLayout />}>
        <Route index
          element={
            <ProtectedRoute>
              <Chat />
            </ProtectedRoute>
          } />
        <Route path="auth" element={<AuthLayout />}>
          <Route path="register" element={<Register />} />
          <Route path="login" element={<Login />} />
        </Route>
      </Route>
    )
  );

  return <RouterProvider router={ router } />;
}

src\contexts\AuthContext.js中,我有:

import { createContext, useEffect, useState } from "react";
import { auth } from "../firebaseConfig";
import { onAuthStateChanged } from "firebase/auth";

export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState({});

  useEffect(() => {
    const unSubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
    });

    return () => {
      unSubscribe();
    };
  }, []);

  return (
    <AuthContext.Provider value={{ currentUser }}>
      {children}
    </AuthContext.Provider>
  );
};

src\layouts\AuthLayout.jsx中,我有:

import * as React from 'react';
import { Outlet } from 'react-router-dom';
import TopBar from '../components/TopBar/TopBar';
import AppFooter from '../components/AppFooter/AppFooter';

export default function AuthLayout() {
  return (
    <div className='App'>
      <TopBar />
      <Outlet />
      <AppFooter />
    </div>
  );
}

src\layouts\RootLayout.jsx中,我有:

import * as React from 'react';
import { Outlet } from 'react-router-dom';

export default function RootLayout() {
  return (
    <div className='App chat-container'>
      <Outlet />
    </div>
  );
}

Register.jsx中,我有:

import React, { useState } from "react";
import md5 from "md5";
import FormCard from "../components/FormCard/FormCard";
import Success from "../components/Alerts/Success";
import Error from "../components/Alerts/Error";
import { make, register } from "simple-body-validator";
import { createUserWithEmailAndPassword, updateProfile } from "firebase/auth";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import { doc, setDoc } from "firebase/firestore";
import { auth, db, storage } from "../firebaseConfig";
import { useNavigate } from "react-router-dom";

// Custom validation rule
register('image-only', function (value) {
  let allowedFormats = ["jpg", "jpeg", "png"];
  let format = value.name.split('.')[1].toLowerCase();
  return allowedFormats.includes(format);
}, function () {
  return "Only JPG, JPEG, and PNG files are allowed.";
});

export default function Register() {

  const navigate = useNavigate();

  const initialFormData = {
    firstName: "",
    lastName: "",
    email: "",
    avatar: "",
    password: "",
    password_confirmation: ""
  };

  const validationRules = {
    firstName: ["required", "string", "min:3", "max:255"],
    lastName: ["required", "string", "min:3", "max:255"],
    email: ["required", "email"],
    avatar: ["image-only"],
    password: ["required", "min:6", "confirmed"],
    password_confirmation: ["required"]
  };

  const validator = make(initialFormData, validationRules);
  const [formData, setFormData] = useState(initialFormData);

  // Form validation errors
  const [errors, setErrors] = useState(validator.errors());
  // Firebase errors
  const [error, setError] = useState(false);

  const [pristineFields, setPristineFields] = useState(() =>
    Object.keys(initialFormData)
  );

  const handleChange = (event) => {
    const { name, value, files } = event.target;
    setFormData((prevFormData) => {
      const newValue = files?.length > 0 ? files[0] : value;
      const newFormData = { ...prevFormData, [name]: newValue };
      const newPristineFields = pristineFields.filter((f) => f !== name);

      validator.setData(newFormData).validate();
      const validationErrors = validator.errors();
      newPristineFields.forEach((f) => validationErrors.forget(f));
      setErrors(validationErrors);

      setPristineFields(newPristineFields);

      return newFormData;
    });
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
  
    if (!validator.setData(formData).validate()) {
      setErrors(validator.errors());
    } else {
      const email = formData.email;
      const password = formData.password;
      const successContainer = document.getElementById('successAlert');
      const errContainer = document.getElementById('errorAlert');
  
      try {
        const response = await createUserWithEmailAndPassword(auth, email, password);
        // Hide error
        errContainer.classList.add('d-none');
  
        // Show success
        successContainer.classList.remove('d-none');
  
        let userAvatar = "default-avatar.png"; // Default avatar image
  
        // Check if the user uploaded an avatar
        if (formData.avatar) {
          // Create avatar filename
          const meta = { contentType: 'image/jpeg, image/png' };
          const date = new Date().getTime();
          userAvatar = md5(`${formData.email}-${date}`) + '.' + formData.avatar.name.split(".")[1];
  
          const storageRef = ref(storage, userAvatar);
  
          // Upload image
          await uploadBytesResumable(storageRef, formData.avatar, meta);
        }
  
        // Get download URL (either uploaded avatar or default avatar)
        const avatarURL = await getDownloadURL(ref(storage, userAvatar));
  
        try {
          // Update profile
          await updateProfile(response.user, {
            photoURL: avatarURL,
          });
  
          // Store user
          await setDoc(doc(db, "users", response.user.uid), {
            uid: response.user.uid,
            firstName: formData.firstName,
            lastName: formData.lastName,
            email: formData.email,
            avatar: userAvatar,
          });

          // If registration was successful, redirect to the Chat UI
          if (response.user.uid) {
            navigate("/");
          }

        } catch (error) {
          setError(true);
          console.log(error);
        }
      } catch (error) {
        setError(true);
        errContainer.append(error.message);
        errContainer.classList.remove('d-none');
      }
    }
  
    setPristineFields([]);
  };

  return (
    <FormCard title="Register">

      {/* Alerts */}
      <Error dismissible={true} error={error} />
      <Success dismissible={false} message="You have registered successfully!" />

      <form onSubmit={handleSubmit}>
        
        // Form fields here
        
      </form>
    </FormCard>
  );
}

问题
下面的代码确实实现了所需的路由更改,但它无法加载<Chat />组件?

if (response.user.uid) {
    navigate("/");
}

我做错了什么?

a2mppw5e

a2mppw5e1#

这个问题的出现是因为React BrowserRouter应该在React树之外定义。将其放在React树中并不是推荐的方法。您可以通过参考文档来验证这一点。
创建浏览器路由器
这是所有React Router Web项目的推荐路由器。它使用DOM历史API来更新URL和管理历史堆栈。
它还支持v6.4数据API,如加载器、操作、获取器等。由于在数据API的设计中提取和渲染的解耦,您应该在React树外部创建路由器,并使用静态定义的路由集。有关此设计的更多信息,请参阅Remixing React Router博客文章和When to Fetch会议演讲。

相关问题