React单页切换登录/注册组件的URL与样式异常问题
React登录/注册页面切换问题:URL不更新与样式失效
我用React开发登录(SignIn)和注册(Signup)页面,要求在同一页面渲染并支持组件切换。已经通过useState实现toggleMode切换功能,但遇到两个问题:
- 切换到注册模式时,URL仍保持为"/Login";
- 切换后页面样式失效,单独使用登录或注册组件时样式显示正常。
问题1:URL不更新的解决办法
当前仅用useState切换组件,没有同步更新路由状态。需要结合react-router-dom的API,实现组件切换与URL同步:
- 在App组件中引入
useNavigate和useLocation; - 初始化组件状态时,根据当前路由路径判断是登录还是注册模式;
- 修改toggleMode函数,切换组件的同时调用
navigate更新URL; - 监听路由变化,同步组件状态,确保手动修改URL时组件能正确切换。
问题2:样式失效的解决办法
Signup和SignIn组件内部都渲染了完整的.container、.forms-container结构,而App组件也重复渲染了这些容器,导致DOM嵌套层级混乱,样式选择器匹配异常。解决方案:
- 重构Signup和SignIn组件,只保留表单核心内容,移除外层容器结构;
- 由App组件作为唯一的布局容器,提供完整的页面结构,子组件仅负责渲染表单。
修改后的Signup组件
import UserServices from '../Services/service'; import React, { useState } from "react"; import toast, {Toaster} from 'react-hot-toast'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import {faCircleExclamation} from '@fortawesome/free-solid-svg-icons'; const Signup = () => { const [firstName , setfirstName] = useState('') const [lastName , setlastName] = useState('') const [birthday , setbirthday] = useState('') const [password , setpassword] = useState('') const [email , setemail] = useState('') const [errors , setErrors] = useState({ firstName : '', lastName : '', email : '', password : '', birthday : '', }); const formValidation = () =>{ const pattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&])[A-Za-z0-9@$!%*?&]{8,}$/; let localErrors = {...errors} if(firstName === ""){ localErrors.firstName = 'First name required'; } if(lastName === ""){ localErrors.lastName = 'Last name required'; } if(email === ""){ localErrors.email = 'Email required'; } if ((password === "") || (!pattern.test(password))){ localErrors.password = 'Password required'; } if(birthday === ""){ localErrors.birthday = 'date required'; } setErrors(localErrors); const isValid = Object.values(localErrors).every(error => error === ''); return isValid; } const signUp = async (e) =>{ e.preventDefault(); if (formValidation()){ const data = { firstName, lastName, email, password, birthday, } try { const response = await UserServices.signup(data) console.log("response ====>", response); toast.success('Successfully created!'); setfirstName(''); setlastName(''); setemail(''); setpassword(''); setbirthday(''); }catch(err){ console.log(err) toast.error('Sign up failed!'); } }else{ console.log("form invalid"); } } return ( <> <Toaster /> <form action="#" className="sign-up-form" onSubmit={signUp}> <h2 className="title">Sign up</h2> <div className="input-field"> <i className="fas fa-user"></i> <input type="text" placeholder="First Name" value={firstName} onChange={ (e)=>setfirstName(e.target.value)}/> {errors.firstName !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.firstName} </div> )} </div> <div className="input-field"> <i className="fas fa-user"></i> <input type="text" placeholder="Last Name" value={lastName} onChange={ (e)=>setlastName(e.target.value)}/> {errors.lastName !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.lastName} </div> )} </div> <div className="input-field"> <i className="fas fa-envelope"></i> <input type="email" placeholder="Email" value={email} onChange={ (e)=>setemail(e.target.value)} /> {errors.email !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.email} </div> )} </div> <div className="input-field"> <i className="fas fa-lock"></i> <input type="password" placeholder="Password" value={password} onChange={ (e)=>setpassword(e.target.value)} /> {errors.password !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.password} </div> )} </div> <div className="input-field"> <i className="fas fa-user"></i> <input type="date" placeholder="Date_of_Birth" value={birthday} onChange={ (e)=>setbirthday(e.target.value)}/> {errors.birthday !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.birthday} </div> )} </div> <input type="submit" className="btn" value="Sign up" /> </form> </> ) } export default Signup
修改后的SignIn组件
import UserServices from '../Services/service'; import React, { useState } from "react"; import toast, {Toaster} from 'react-hot-toast'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import {faCircleExclamation} from '@fortawesome/free-solid-svg-icons'; const SignIn = () => { const [password , setpassword] = useState('') const [email , setemail] = useState('') const [errors , setErrors] = useState({ email : '', password : '', }); const formValidation = () =>{ const pattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&])[A-Za-z0-9@$!%*?&]{8,}$/; let localErrors = {...errors} if(email === ""){ localErrors.email = 'Email required'; } if ((password === "") || (!pattern.test(password))){ localErrors.password = 'Password required'; } setErrors(localErrors); const isValid = Object.values(localErrors).every(error => error === ''); return isValid; } const signin = async (e) =>{ e.preventDefault(); if (formValidation()){ const data = { email, password, } try { const response = await UserServices.signin(data) console.log("response ====>", response); toast.success('Successfully logged in!'); setemail(''); setpassword(''); }catch(err){ console.log(err) toast.error('Login failed!'); } }else{ console.log("form invalid") } } return ( <> <Toaster /> <form action="#" className="sign-in-form" onSubmit={signin}> <h2 className="title">Sign in</h2> <div className="input-field"> <i className="fas fa-user"></i> <input type="text" placeholder="Email" value={email} onChange={ (e)=>setemail(e.target.value)}/> {errors.email !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.email} </div> )} </div> <div className="input-field"> <i className="fas fa-lock"></i> <input type="password" placeholder="Password" value={password} onChange={ (e)=>setpassword(e.target.value)} /> {errors.password !== '' && ( <div className="error-message"> <FontAwesomeIcon icon={faCircleExclamation} /> {errors.password} </div> )} </div> <input type="submit" value="Login" className="btn solid" /> </form> </> ) } export default SignIn;
修改后的App组件
import './App.css'; import Signup from './pages/signup.js'; import SignIn from './pages/login.js'; import img1 from './pages/students.png'; import img2 from './pages/students2.png'; import { useState, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; function App() { const navigate = useNavigate(); const location = useLocation(); const [isSignUpMode, setIsSignUpMode] = useState(location.pathname === '/signup'); useEffect(() => { // 监听路由变化,同步组件状态 setIsSignUpMode(location.pathname === '/signup'); }, [location.pathname]); const toggleMode = () => { const newMode = !isSignUpMode; setIsSignUpMode(newMode); // 更新路由URL navigate(newMode ? '/signup' : '/login'); }; return ( <div className={`container ${isSignUpMode ? 'sign-up-mode' : ''}`}> <div className="forms-container"> <div className="signin-signup"> {isSignUpMode ? <Signup /> : <SignIn />} </div> </div> <div className="panels-container"> <div className="panel left-panel"> <div className="content"> <h3>New in our faClub ?</h3> <p>Enter your personal details and start journey with us.</p> <button className="btn transparent" onClick={toggleMode}>Sign up</button> </div> <img src={img1} className="image" alt="" /> </div> <div className="panel right-panel"> <div className="content"> <h3>One of our faClub ?</h3> <p>To keep connected with us please login with your personal info.</p> <button className="btn transparent" onClick={toggleMode}>Sign in</button> </div> <img src={img2} className="image" alt="" /> </div> </div> </div> ) } export default App;
路由配置(示例)
// index.js import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <BrowserRouter> <Routes> <Route path="/login" element={<App />} /> <Route path="/signup" element={<App />} /> {/* 默认重定向到登录页 */} <Route path="/" element={<Navigate to="/login" />} /> </Routes> </BrowserRouter> );
内容的提问来源于stack exchange,提问作者Ayouni Amir
相关产品推荐
相关产品推荐

