0

I am using nextjs, I have a page where it lists out all clients, I can then click each client name that links (using next/link) to a page specifically for that client. It loads correctly using useEffect and useState. If I refresh manually all data loads correctly. Yet If I open the "add invoice" form and submit, the submission goes through correctly to the database, yet on refresh (from clicking submit) the page loads none of the original information on the initial fetch. The page, clients.js, has a component that has a query part that sends the client id and name to the [id].js page, eventually sent to the server via the fetchInvoices function. [id].js calls clientTitle.jsx for the add invoice button that holds the form and submit button. I have been working on this site so much to prove to myself that I can actually code and create instead of just copying youtube tutorials so it would be great if I could get help thanks.

NOTE see <==== in code for important info

My debugging effort: After doing some debugging, the router.query in [id].js retains the information for the id but not for the name upon submitting, this is important as for the fetch to function correctly the individual invoices for the client to be fetched the name is needed. So why does the id stay stored but not the name from the query, is it because only the id part is in the URL link, since [id].js in react renames that to the actual Id? Thats my thinking but unsure. I am sure this is the problem.

Code layout: This is the main client page with all clients layout | FILENAME: clients.js

const clients = ({ clients }) => {
  return (
    <div>
      <Title title="Clients" />
      <div>
        {clients.map((client) => (

       //Maps out all the clients in the database//
          <div key={client._id}>
            {/* <Link href="/clientInfo"> */}
            <Link
              href={{
                pathname: "/clients/[id]",
                query: {
                  id: client._id,    <=============================
                  name: client.name, <=======
                },
              }}
            >
              <div >
                <a>{client.name}</a>
              </div>
            </Link>

            <span>{client.balance}</span>
            <span>{client.trips}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

After clicking a client name it opens this page | FILENAME: [id].js //used like this so id shows up as part of URL

import ClientTitle from "./ClientTitle";

const ClientInfo = () => {
  const router = useRouter();
  const { id, name } = router.query; //gets slug info from url <=======================

  const [clientData, setClientData] = useState({});
  const [clientInvoices, setClientInvoices] = useState(null);

  //fetches data sets to state
  const fetchData = async () => {
    const data = await axios.get(`http://localhost:5010/clients/${id}`);
    setClientData(data.data);

    return data;
  };

  const fetchInvoices = async () => {.  <======================
    //get client specific invoices
    const invoices = await axios.get(
      `http://localhost:5010/clients/${id}/fetch?client=${name}` <=====taken from top
    );
    console.log(id);
    console.log(name);
    console.log(invoices);
    console.log("fetching");
    setClientInvoices(invoices.data);
  };

  //runs at start of page and refresh dependent on router readiness
  useEffect(() => {
    if (router.isReady) {
      fetchData();
      fetchInvoices();
    }
  }, [router.isReady]);

  return (
    <div>
      <ClientTitle       <==============
        title={clientData.name}
        balance={clientData.balance}
        clientId={clientData.clientId}
      />
      <div>
        <div>
          <span>Date</span>
          <span>From</span>
          <span>To</span>
          <span>Price</span>
        </div>
        {clientInvoices &&
          clientInvoices.map((invoice) => (
            <div key={invoice._id}>
              <div>{invoice.date}</div>
              <div>{invoice.from}</div>
              <div>{invoice.to}</div>
              <div>{invoice.price}</div>
            </div>
          ))}
      </div>
    </div>
  );
};

export default ClientInfo;

In the above code it calls this code below in another file, for the top nav bar with the submission form | FILENAME: ClientTitle.jsx

const Form = ({ name }) => {
  const [clientInvoice, setClientInvoice] = useState({
    client: name,
    date: "",
    from: "",
    to: "",
    quantity: null,
    loadType: "",
    price: null,
    note: "",
  });

  const handleSubmit = async (e) => {
    await axios.post("http://localhost:5010/clients/invoices", clientInvoice);
  };

  return (
    <div className="relative">
      <div>
        <span>New Invoice</span>
        <form onSubmit={handleSubmit}>
          <input
            placeholder="date"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, data: e.target.value })
            }
          />
          <input
            placeholder="from"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, from: e.target.value })
            }
          />
          <input
            placeholder="to"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, to: e.target.value })
            }
          />
          <input
            placeholder="quantity"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, quantity: e.target.value })
            }
          />
          <input
            placeholder="boxes/pallets"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, loadType: e.target.value })
            }
          />
          <input
            placeholder="price"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, price: e.target.value })
            }
          />
          <input
            placeholder="note"
            onChange={(e) =>
              setClientInvoice({ ...clientInvoice, note: e.target.value })
            }
          />
          <button
            type="submit"
          >
            Create
          </button>
        </form>
      </div>
    </div>
  );
};

const ClientTitle = ({ title, balance }) => {
  const [showForm, setShowForm] = useState(false);

  const handleForm = () => {
    setShowForm(!showForm);
  };

  return (
    <div >
      <div >
        <h1 >{title}</h1>
        <span ">Total Balance: {balance}</span>
        <button
          onClick={() => handleForm()}
        >
          <span >Add Invoice</span>
        </button>
      </div>
      <div />
      {showForm && <Form name={title} />}
    </div>
  );
};

export default ClientTitle;

1 Answer 1

1

The first thing I would change would be the link href. Basically the [id] part means that is expects something like clients/123 where 123 would be client._id. So something like:

        <Link
          href={{
            pathname: `/clients/${client._id}`,
            query: {
              name: client.name,
            },
          }}
        >

In this way the id is added automatically and your link will look something like http://localhost:3000/123?name=clientname (for a client with id 123 and name clientname).

I also created a minimal reproducible example here.

Another potential issue is that you are not preventing the default on form submit. So try either e.preventDefault() in your handlesubmit like so:

  const handleSubmit = async (e) => {
    e.preventDefault();

    await axios.post("http://localhost:5010/clients/invoices", clientInvoice);
  };

If your problem still persist I recommend to check if your link changes in any way and maybe investigate if there is something else that might alter it. Additionally if you still can't figure it out try to create a minimal reproducible example.


Edit:

Form submit method, by default, is appending the input values to the url and then is sending a request (and therefore replacing the name attribute in the link).

According to this article

The Name Attribute for Notice that each input field must have a name attribute to be submitted.

If the name attribute is omitted, the value of the input field will not be sent at all.

In your case your inputs have only placeholder and onChange. If you add a name to the inputs it will be most likely appended to the link when submitting.

Another mention would be that all your inputs should have a value set-up (for date it should be value={clientInvoice.date} for ex.).

EDIT 2 + Warning: return false won't work according to react docs about handling events (in vanilla js you can also return false at the end of the callback to prevent the default behavior)

4
  • Did the change to the link, shows up still correct. Was not able to fix the form submission resetting the information. Working on a mock up on code sandbox. From your Url link change it is clear now that what happens is that when the form is submiited the url changes from /123?name=clientname to just /123? . Not sure why the url would change, anyway possible to retain the name param after submission? I'll edit it when the code sandbox is ready. Thx
    – tester0001
    Sep 6, 2022 at 15:42
  • Have you tried both event.preventDefault() and return false at the end of handleSubmit ? What happens is that form onsubmit adds the form input data to the link by default and therefore it replaces the name=clientname part at the end of the link. I will edit my comment with some details in that direction
    – Berci
    Sep 6, 2022 at 17:05
  • Followed your edit and the submitted data now stays in the url after submission, all except the name of the client. I did brute solve this by adding a <input name="name" value={clientInvoice.client} /> now on refresh the client name stays and renders the data. Im using tailwind css so a simple "hidden" removed the name from showing on the form. Is this the only way to do it? I feel like I'm doing it the wrong way, more steps than actually needed. Also is it not bad that the url dramatically now shows the submitted payload? Yes the e.prevent/return false did not work, prevented submission.
    – tester0001
    Sep 6, 2022 at 18:02
  • You should be able to prevent the default behavior and therefore the replacing of the link (and therefore without any complications). But is really hard to debug what is going wrong without a minimal reproducible example. Another thing you could do is making the button type="button" and calling handleSubmit on button onClick. That is also a little hacky but better than adding an extra input.
    – Berci
    Sep 7, 2022 at 9:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.