javascriptreactjsforms

How can I set form submit button to disabled until form is empty using useActionState hook?


I have a form in a React app. I am using the useActionState hook to manage form status. How can I set the submit button to disabled until the user enters something in the text input?

By using the useState hook to store the input and calling the setter method on input change I can track if there is an input in the form field. But by using useActionState I can only know the form values after submitting the form.

import { useActionState, useState } from "react";

export function MyForm() {

  // form state managed with useActionState
  const [state, formAction] = useActionState(
    updateState,
    { name: ""}
  )
  function updateState(_prevState, formData) {
    // do something
    return {name: formData.get("name") as string}
  }

  // form state managed with useState
  const [state2, setState2] = useState({name: ""})
  function handleChange(e) {
    setState2({name: e.target.value})
  }
  function handleSubmit(e) {
    e.preventDefault()
    // do something
    setState2({name: ""})
  }

  return (
    <>
      {/* form state managed with useActionState */}
      <form  action={formAction}>
        <input type="text" name="name" />
        <button type="submit">Submit</button> {/* <- I want this to be disabled until user enters a value */}
        <div>{state.name}</div>
      </form>

      {/* form state managed with useState */}
      <form onSubmit={handleSubmit}>
        <input type="text" name="name" onChange={handleChange} value={state2.name}/>
        <button disabled={state2.name ? false : true} type="submit">Submit</button> 
        <div>{state2.name}</div>
      </form>
    </>
  )
}

I know there is a hook useFormStatus which might be used for this but I don't like the idea of having the content of my form in a separate child component.


Solution

  • Both useActionState and useFormStatus hooks gives you some ways to use state of a form after submission. none of them will give you ability to work with states before a form submission. So, if you want to disable your button if an input is empty, you need to use useState to check for input's value and disable button on any change. For example:

    import { useActionState, useState } from "react";
    
    export function MyForm() {
    
      // form state managed with useActionState
      const [state, formAction] = useActionState(
        updateState,
        { name: ""}
      )
      function updateState(_prevState, formData) {
        // do something
        return {name: formData.get("name") as string}
      }
    
      // form state managed with useState
      const [inputValue, setInputValue] = useState(null)
      function handleChange(e) {
        setInputValue(e.currentTarget.value)
      }
    
      return (
          <form action={formAction}>
            <input type="text" name="name" onChange={handleChange} />
            <button type="submit" disabled={!inputValue}>Submit</button>
            <div>{state.name}</div>
          </form>
      )
    }