skip to Main Content

I’m new to React and front end in general, so I’m learning everything from square zero and I’ve hit a barrier I can’t solve by myself. What I’m trying to do now is to redirect after submitting a form. I’m using react-router-dom v.6.9.0 and most of the information I find is from previous versions which has not worked with the way I’ve structured my code. My files look as follow:

form.jsx

import React from 'react';
import { useForm } from 'react-hook-form';
import {useState} from 'react';
import { redirect, useNavigate } from 'react-router-dom';

function onSubmit(data){
    const navigate = useNavigate();
    alert(JSON.stringify(data, null, 4));
    fetch("/api/submitHo", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body:JSON.stringify(data)
    }).then(
        response => response.json()
    ).then(
        res => {
            console.log(res);
            navigate("/");})
}

*/.../*

function TheForm() {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm();
  
*/.../*

  return (
    <React.Fragment>
        <form onSubmit={handleSubmit((data) =>onSubmit(data))}>
           */.../* 
        </form>
    </React.Fragment>
  );
}

export default TheForm

root.jsx

import { Outlet, Link } from "react-router-dom";

export default function Root() {
  return (
    <>
      <div id="sidebar">
        <nav>
          <ul>
          <li>
              <Link to={`/new-form`}>NEW HO Input</Link>
            </li>
            <li>
              <Link to={`/ho-viewer`}>HO Viewer</Link>
            </li>
          </ul>
        </nav>
      </div>
            <div id="detail">
        <Outlet />
      </div>
    </>
  );
}

index.js

import React from "react";
import ReactDOM from "react-dom/client";
import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";
import "./index.css";
import ErrorPage from "./errorPage";
import HoViewer from "./routes/hoViewer";
import Root from "./routes/root";
import TheForm from "./routes/form";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: "/ho-viewer",
        element: <HoViewer/>,
        errorElement: <ErrorPage />,
      },
      {
        path: "/new-form",
        element: <TheForm />,
        errorElement: <ErrorPage />,
      },
    ],
  },

]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);


Without the useNavigate line in form.jsx the post request works perfectly fine. However when I try to redirect using useNavigate it returns the following error:

React Hook "useNavigate" is called in function "onSubmit" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use"

I’m not sure how to modify my files to make it work. I’ve been trying to implement other solutions from stackoverflow without much success. Could you please guide me on understanding how to redirect using a function that’s not a React component or a custom hook?

I’m using express on the backend but I don’t think that is much relevant in this problem.

2

Answers


  1. I have two ideas here.

    1. Move the declaration of useNavigate outside of the function. So it would look like this:
    import React from 'react';
    import { useForm } from 'react-hook-form';
    import {useState} from 'react';
    import { redirect, useNavigate } from 'react-router-dom';
    
    export default MyReactComponent(){
     const navigate = useNavigate();
    
    function onSubmit(data){
        alert(JSON.stringify(data, null, 4));
        fetch("/api/submitHo", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body:JSON.stringify(data)
        }).then(
            response => response.json()
        ).then(
            res => {
                console.log(res);
                navigate("/");})
    }
    }
    

    You shouldn’t be calling your main component function inside of itself.
    Like what you do here:

      return (
        <React.Fragment>
            <form onSubmit={handleSubmit((data) =>onSubmit(data))}>
               */.../* 
            </form>
        </React.Fragment>
    );
    
    1. You could try using redirect by creating a return home function and calling that instead of useNavigate:
    import React from 'react';
    import { useForm } from 'react-hook-form';
    import {useState} from 'react';
    import { redirect, useNavigate } from 'react-router-dom';
    
    function returnHome(){
    return redirect("/");
    }
    
    function onSubmit(data){
        const navigate = useNavigate();
        alert(JSON.stringify(data, null, 4));
        fetch("/api/submitHo", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body:JSON.stringify(data)
        }).then(
            response => response.json()
        ).then(
            res => {
                console.log(res);
                returnHome();})
    }
    
    Login or Signup to reply.
  2. React hooks can only be called from React function components or custom React hooks. You’ll need to move the useNavigate hook out of the callback, and move both the useNavigate hook and callback into the React function component.

    import React from 'react';
    import { useForm } from 'react-hook-form';
    import { useState } from 'react';
    import { useNavigate } from 'react-router-dom';
    
    ...
    
    function TheForm() {
      const {
        register,
        handleSubmit,
        watch,
        formState: { errors },
      } = useForm();
    
      const navigate = useNavigate();
    
      function onSubmit(data) {
        alert(JSON.stringify(data, null, 4));
        fetch("/api/submitHo", {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(data)
        })
          .then(response => response.json())
          .then(res => {
            console.log(res);
            navigate("/");
          });
      }
      
      ...
    
      return (
        <React.Fragment>
          <form onSubmit={handleSubmit(onSubmit)}>
            */.../* 
          </form>
        </React.Fragment>
      );
    }
    
    export default TheForm;
    

    If you are trying to define the submit function externally for code-reuse then refactor it a bit to consume a passed navigate function.

    import React from 'react';
    import { useForm } from 'react-hook-form';
    import { useState } from 'react';
    import { useNavigate } from 'react-router-dom';
    
    const onSubmit = (navigate) => (data) => {
      alert(JSON.stringify(data, null, 4));
      fetch("/api/submitHo", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
      })
        .then(response => response.json())
        .then(res => {
          console.log(res);
          navigate("/");
        });
    };
    
    ...
    
    function TheForm() {
      const {
        register,
        handleSubmit,
        watch,
        formState: { errors },
      } = useForm();
      
      const navigate = useNavigate();
    
      ...
    
      return (
        <React.Fragment>
            <form onSubmit={handleSubmit(onSubmit(navigate))}>
               */.../* 
            </form>
        </React.Fragment>
      );
    }
    
    export default TheForm;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search