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
- Redux State Update Asynchronicity: When you
await dispatch(checkValidUser(email)), the Redux state (and thus your component'sprops.message) doesn't update immediately. Trying to accessthis.props.messageright after dispatch gives you the old value. - Missing Registration Logic: You're only validating the email existence but not triggering the actual registration when the email is new.
- No History Access: Your component needs access to React Router's
historyto 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
- User submits the registration form.
- Frontend validation runs first. If any check fails, an error toast is shown immediately.
- If validation passes,
checkValidUseraction is dispatched to verify email existence. - Once Redux state updates with the validation message,
componentDidUpdatedetects the change:- If email exists: Show
toastError("User already exists")and clear the message. - If email is new: Dispatch
registerUserwith the form data. After successful registration, the redirect callback triggers and sends the user to the login page, whiletoastSuccessfires from theregisterUseraction.
- If email exists: Show
内容的提问来源于stack exchange,提问作者hellraiser999
相关产品推荐
相关产品推荐

