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

React单页切换登录/注册组件的URL与样式异常问题

React登录/注册页面切换问题:URL不更新与样式失效

我用React开发登录(SignIn)和注册(Signup)页面,要求在同一页面渲染并支持组件切换。已经通过useState实现toggleMode切换功能,但遇到两个问题:

  • 切换到注册模式时,URL仍保持为"/Login";
  • 切换后页面样式失效,单独使用登录或注册组件时样式显示正常。

问题1:URL不更新的解决办法

当前仅用useState切换组件,没有同步更新路由状态。需要结合react-router-dom的API,实现组件切换与URL同步:

  1. 在App组件中引入useNavigate和useLocation;
  2. 初始化组件状态时,根据当前路由路径判断是登录还是注册模式;
  3. 修改toggleMode函数,切换组件的同时调用navigate更新URL;
  4. 监听路由变化,同步组件状态,确保手动修改URL时组件能正确切换。

问题2:样式失效的解决办法

Signup和SignIn组件内部都渲染了完整的.container、.forms-container结构,而App组件也重复渲染了这些容器,导致DOM嵌套层级混乱,样式选择器匹配异常。解决方案:

  1. 重构Signup和SignIn组件,只保留表单核心内容,移除外层容器结构;
  2. 由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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:02:32