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

React注册组件邮箱校验提示与登录跳转功能实现求助

Solution for Email Validation & Registration Flow

Let's break down the issues in your current code and fix them step by step:

Key Issues Identified

  1. Redux State Update Asynchronicity: When you await dispatch(checkValidUser(email)), the Redux state (and thus your component's props.message) doesn't update immediately. Trying to access this.props.message right after dispatch gives you the old value.
  2. Missing Registration Logic: You're only validating the email existence but not triggering the actual registration when the email is new.
  3. No History Access: Your component needs access to React Router's history to redirect to the login page after successful registration.

Step 1: Update Component to Handle State Changes

Use componentDidUpdate to listen for changes to props.message and act accordingly. This ensures you're working with the latest state from Redux.

Modify your RegistrationForm.js component:

import React, { Component } from "react";
import { registerUser, checkValidUser } from "../actions/userActions";
import { connect } from "react-redux";
import validator from "validator";
import { Link, withRouter } from "react-router-dom"; // Add withRouter
import { toastError, toastSuccess } from "../../utils/toastify";

class RegistrationForm extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      email: "",
      password: "",
    };
  }

  handleChange = (event) => {
    const { name, value } = event.target;
    this.setState({
      [name]: value,
    });
  };

  componentDidUpdate(prevProps) {
    // Only run when message changes and has a value
    if (prevProps.message !== this.props.message && this.props.message) {
      const { message } = this.props;
      
      if (message === "User already exists") {
        toastError(message);
        // Clear the message to avoid re-triggering on next update
        this.props.dispatch({ type: "CLEAR_REGISTRATION_MESSAGE" });
      } else if (message === "User does not exists") {
        // Email is new, proceed with registration
        const registrationData = {
          username: this.state.username,
          email: this.state.email,
          password: this.state.password,
        };
        // Dispatch registerUser with redirect callback
        this.props.dispatch(registerUser(registrationData, () => {
          this.props.history.push("/login");
        }));
      }
    }
  }

  handleSubmit = async (event) => {
    event.preventDefault();
    const { username, email, password } = this.state;

    // Keep all your existing frontend validation checks
    if (!username || !email || !password) {
      return toastError("Credentials should not be empty");
    }
    if (username.length < 6) {
      return toastError("Username should be greater than 6 characters.");
    }
    if (!validator.isEmail(email)) {
      return toastError("Invalid email.");
    }
    if (password.length < 6) {
      return toastError("Password must contain 6 characters.");
    }

    // Only trigger email validation, registration will happen in componentDidUpdate
    await this.props.dispatch(checkValidUser(email));
  }

  render() {
    const isRegistrationInProgress = this.props.isRegistrationInProgress;
    return (
      <div>
        <div className="field">
          <p className="control has-icons-left has-icons-right">
            <input onChange={this.handleChange} name="username" value={this.state.username} className="input" type="text" placeholder="Username" />
            <span className="icon is-small is-left">
              <i className="fas fa-user"></i>
            </span>
          </p>
        </div>
        <div className="field">
          <p className="control has-icons-left has-icons-right">
            <input onChange={this.handleChange} name="email" value={this.state.email} className="input" type="email" placeholder="Email" />
            <span className="icon is-small is-left">
              <i className="fas fa-envelope"></i>
            </span>
          </p>
        </div>
        <div className="field">
          <p className="control has-icons-left">
            <input onChange={this.handleChange} name="password" value={this.state.password} className="input" type="password" placeholder="Password" />
            <span className="icon is-small is-left">
              <i className="fas fa-lock"></i>
            </span>
          </p>
        </div>
        <div className="field">
          <div className="control">
            {isRegistrationInProgress ? (
              <button className="button is-success is-loading">Sign Up</button>
            ) : (
              <button onClick={this.handleSubmit} className="button is-success">
                Sign up
              </button>
            )}
            <Link to="/login">
              <p className="has-text-danger">
                Already have an account? Sign In
              </p>
            </Link>
          </div>
        </div>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    isRegistrationInProgress: state.registration.isRegistrationInProgress,
    message: state.registration.message,
  };
};

// Wrap with withRouter to access history prop
export default withRouter(connect(mapStateToProps)(RegistrationForm));

Step 2: Add Message Clear Action to Reducer

Update your registration reducer to handle clearing the message after processing it, preventing duplicate toasts:

const initialState = {
  isRegistrationInProgress: false,
  isRegistered: false,
  registrationError: null,
  user: {},
  isValidating: false,
  isValidated: false,
  validationError: null,
  message: "",
}

const registration = (state = initialState, action) => {
  switch (action.type) {
    case "REGISTRATION_STARTS":
      return {
        ...state,
        isRegistrationInProgress: true,
        registrationError: null,
      }
    case "REGISTRATION_SUCCESS":
      return {
        ...state,
        isRegistrationInProgress: false,
        registrationError: null,
        isRegistered: true,
        user: action.data,
      }
    case "REGISTRATION_ERROR":
      return {
        ...state,
        isRegistrationInProgress: false,
        registrationError: action.data.error,
        isRegistered: false,
        user: {},
      }
    case "CHECK_VALID_USER_STARTS":
      return {
        ...state,
        isValidating: true,
        isValidated: false,
        validationError: null,
      }
    case "CHECK_VALID_USER_SUCCESS":
      return {
        ...state,
        isValidating: false,
        isValidated: true,
        message: action.data.message,
      }
    case "CHECK_VALID_USER_ERROR":
      return {
        ...state,
        validationError: action.data.error,
      }
    case "CLEAR_REGISTRATION_MESSAGE":
      return {
        ...state,
        message: "",
      };
    default:
      return state;
  }
}

export default registration;

Step 3: Fix Minor Controller Typo (Optional)

In your checkValidUser controller, fix the typo in the message for consistency:

checkValidUser: async (req, res, next) => {
  console.log("inside check valid user controller")
  const { email } = req.params
  console.log(email)
  try {
    const user = await User.findOne({ email })
    if (user) {
      return res.status(200).json({ message: "User already exists" })
    } else {
      return res.json({ message: "User does not exist" }) // Fixed "exists" to "exist"
    }
  } catch (error) {
    return next(error)
  }
}

How It Works Now

  1. User submits the registration form.
  2. Frontend validation runs first. If any check fails, an error toast is shown immediately.
  3. If validation passes, checkValidUser action is dispatched to verify email existence.
  4. Once Redux state updates with the validation message, componentDidUpdate detects the change:
    • If email exists: Show toastError("User already exists") and clear the message.
    • If email is new: Dispatch registerUser with the form data. After successful registration, the redirect callback triggers and sends the user to the login page, while toastSuccess fires from the registerUser action.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 14:27:28