Search code examples
reactjsnext.js

How to set form default values on nextjs using server actions


I have a server action form, and I want it to be a reusable form.

// File: success_indicator/successIndicatorForm/SuccessIndicatorForm

"use client";

import SubmitBtn from "@/app/components/SubmitBtn";
import React, {useEffect, useState} from "react";

const SuccessIndicatorForm = ({state, formAction, formValues}: any) => {
  
  const [dataForm, setFormValues] = useState({
    major_final_output: "",
    title: "",
    target_measure: "",
  });

  useEffect(() => {
    console.log("wwwwwwww");
    setFormValues({
      major_final_output: "asd",
      title: "ww",
      target_measure: "123",
    });
  }, []);

  const changeHandler = (e: any) => {
    setFormValues({...dataForm, [e.target.name]: e.target.value});
  };

  return (
    <div className="max-w-max px-4 py-8 bg-white rounded-lg shadow dark:bg-gray-800 sm:px-6 md:px-8 lg:px-10 m-auto mt-20 text-white">
      <form action={formAction} className="flex flex-col w-full justify-around">
        <div className="relative">
          <div className="flex flex-col justify-between">
            <div>
              <label htmlFor="major_final_output">Major Final Output:</label>
              <input
                value={dataForm.major_final_output} <----------!!!!! ERROR WHEN USING THIS value prop
                onChange={changeHandler}
                type="text"
                id="major_final_output"
                name="major_final_output"
                placeholder="MFO 1: Human Resource Management and Development Services"
                className="rounded-lg py-1 px-1 m-2 text-black"
                required
              />
            </div>


          <SubmitBtn />
        </div>
        <p role="status">{state?.message}</p>
      </form>
    </div>
  );
};

export default SuccessIndicatorForm;

but I want to use it on creating new and updating data. For now this works when creating new data. but when I tried to add a value property on input I will get this error.

Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
input
div
div
div
form
div

Also the setFormValues won't update the form values but I can confirm it's running through log('wwwwwwww').

So my idea is using SuccessIndicatorForm and passing a form value props to set the values.


Solution

  • I think your mixing server and client components. Your component specify "use client".

    I think you should seperate into three components:

    1. SuccessIndicatorFormCreate & SuccessIndicatorFormUpdate, the real server components containing the handlers for the create/update action
    2. SuccessIndicatorFormContent, the inner form agnostic for create/update

    Also, from your example, I don't see a valid reason using useState & useEffect.

    So I think you should have something like this

    // success_indicator/successIndicatorForm/SuccessIndicatorFormCreate
    const SuccessIndicatorFormCreate = ({ formValues }) => {
      async function formAction(formData) {
        "use server";
        // ...
      }
    
      return (
        <form action={formAction} className="flex flex-col w-full justify-around">
          <SuccessIndicatorFormContent formValues={formValues} />
        </form>
      );
    };
    
    // success_indicator/successIndicatorForm/SuccessIndicatorFormUpdate
    const SuccessIndicatorFormUpdate = ({ formValues }) => {
      async function formAction(formData) {
        "use server";
        // ...
      }
    
      return (
        <form action={formAction} className="flex flex-col w-full justify-around">
          <SuccessIndicatorFormContent formValues={formValues} />
        </form>
      );
    };
    
    // success_indicator/successIndicatorForm/SuccessIndicatorFormContent
    const SuccessIndicatorFormContent = ({ formValues }: any) => {
      return (
        <div className="relative">
          <div className="flex flex-col justify-between">
            <div>
              <label htmlFor="major_final_output">Major Final Output:</label>
              <input
                defaultValue={formValues.major_final_output}
                type="text"
                id="major_final_output"
                name="major_final_output"
                placeholder="MFO 1: Human Resource Management and Development Services"
                className="rounded-lg py-1 px-1 m-2 text-black"
                required
              />
            </div>
          </div>
        </div>
      );
    };