react-hooksreact-routerreact-router-domreact-typescriptreact-router-component

Registration Form's onSubmit with Link from React-Router-Dom


What I'm trying to achieve is that I want a user to be able to submit a registration only when they follow the correct guidelines, such as having a minimum of 8 characters, etc. I have a button set up with a Link router:

//CSS
import "./Register.css"
//LOCAL LIBRARIES
import { VoidFunc, ChangeEventHandlerType, 
FormEventHandlerType } from "./index"
import useSubmit from "../../Hooks/Register/useSubmit";
//EXTERNAL LIBRARIES
import { Link } from "react-router-dom";

const Register = () => {

    {...}

  //Manage Admin
  const [username, password, confirmPassword, handleChange, 
  handleSubmit, placeholder, error, registerUser] = useSubmit()

  return (

      <div className="div-register">

          {...}

        <form className="form-register" 
        onSubmit={handleSubmit as FormEventHandlerType}>

          {...}

              <Link className="link-register" to={registerUser as string}>
                <button className="btn-submit-register">Submit</button>
              </Link>

            {...}

        </form>

      </div>
  );
};

export default Register;

My attempt was to use a useState hook to manage the url destination of the component so that when the input fields are not filled in properly, the Links "to" property is set to "/register" (keeping the user on the same page they are already on), and when they are, it is set to "/admin" which is the main logged in destination.

I have another .tsx file where I refactored a few elements into one arrow function. The bits I cut out just had conditions like "If the user didnt fill out a field, add an error text to the placeholder for that input field":

//LOCAL
import RegisterUser from "../../Components/Register/RegisterUser";
//EXTERNAL
import { useState } from "react";
import {iState, StateType, 
ChangeEventType, FormEventType} from         
"../../Pages/Register/index"

const useSubmit = () =>{

    //Number Check
    let hasNumber = /\d/;  

    //Error
    const [error, setError] = useState<string>("")

    //Is Password valid
    const [registerUser, setRegisterUser] = useState<string>("/register")

    //Placeholder
    const [placeholder, setPlaceholder] = useState<string>("")

    //Manage Admin
    const [state, setState] = useState<StateType>(iState)
    const {username, password, confirmPassword} = state

    const handleChange = (event: ChangeEventType) => {
        //unfinished code
        event.preventDefault()
        const {name, value} = event.target
        setState({...state, [name]: value})
        console.log(state)

    }

    const handleSubmit = (event: FormEventType) => {

        event.preventDefault()

    {...FAILURE CONDITIONS}
    
    //SUCCESS BLOCK
    else {
        setPlaceholder("")
        setError("")
        setRegisterUser("/admin")
    }

    }

  return [username, password, 
  confirmPassword, handleChange, handleSubmit, placeholder, 
  error, registerUser]
}

export default useSubmit;

Any help is appreciated!


Solution

  • It seems you are really just wanting to navigate to "/admin" from the submit handler. For this you should use an imperative navigation instead of the Link. (1) the Link's click handler won't wait for the form element's onSubmit handler to complete, and (2) it certainly can't wait for the registerUser state to update.

    Use the useHistory hook to access the history object if you are using react-router-dom@5 or the useNavigate hook to access the navigate function if you are using react-router-dom@6.

    useSubmit

    Import and use either useHistory or useNavigate hooks, depending on installed version, and invoke in the submit handler on the success logic branch. Remove the registerUser state. Return an object instead of an array so destructuring assignment doesn't depend on array indices.

    import { useHistory } from 'react-router-dom';  // RRDv5
    import { useNavigate } from 'react-router-dom'; // RRDv6
    
    const useSubmit = () => {
      const history = useHistory();   // RRDv5
      const navigate = useNavigate(); // RRDv6
    
      // Number Check
      let hasNumber = /\d/;  
    
      // Error
      const [error, setError] = useState<string>("");
    
      // Placeholder
      const [placeholder, setPlaceholder] = useState<string>("");
    
      // Manage Admin
      const [state, setState] = useState<StateType>(iState);
      const {username, password, confirmPassword} = state;
    
      const handleChange = (event: ChangeEventType) => {
        // unfinished code
        event.preventDefault()
        const { name, value } = event.target;
        setState(state => ({
          ...state,
          [name]: value
        }));
      }
    
      const handleSubmit = (event: FormEventType) => {
        event.preventDefault()
    
        {...FAILURE CONDITIONS}
        
        // SUCCESS BLOCK
        else {
          history.replace("/admin");             // RRDv5
          navigate("/admin", { replace: true }); // RRDv6
        }
      }
    
      return {
        username,
        password, 
        confirmPassword,
        handleChange,
        handleSubmit,
        placeholder, 
        error,
      };
    };
    

    Register

    Remove the Link component wrapping the submit button, unnecessary. Use object destructuring assignment from the useSubmit hook.

    const Register = () => {
      ...
    
      // Manage Admin
      const {
        username,
        password,
        confirmPassword,
        handleChange, 
        handleSubmit,
        placeholder,
        error,
      } = useSubmit();
    
      return (
        <div className="div-register">   
          ...
          <form
            className="form-register" 
            onSubmit={handleSubmit as FormEventHandlerType}
          >
            ...
            <button className="btn-submit-register">Submit</button>
            ...
          </form>
        </div>
      );
    };