我正在用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("/");
}
我做错了什么?
1条答案
按热度按时间a2mppw5e1#
这个问题的出现是因为React BrowserRouter应该在React树之外定义。将其放在React树中并不是推荐的方法。您可以通过参考文档来验证这一点。
创建浏览器路由器
这是所有React Router Web项目的推荐路由器。它使用DOM历史API来更新URL和管理历史堆栈。
它还支持v6.4数据API,如加载器、操作、获取器等。由于在数据API的设计中提取和渲染的解耦,您应该在React树外部创建路由器,并使用静态定义的路由集。有关此设计的更多信息,请参阅Remixing React Router博客文章和When to Fetch会议演讲。