Aashutosh Kumar
Aashutosh Kumar

Reputation: 81

How to reset the state returned from useFormState in react js?

First of all here is my code in react

import { useFormState, useFormStatus } from 'react-dom'
import { submitAction } from './action.ts'

export default function Calculator() {
  const [result, submitAction] = useFormState(submitForm, null)

  return (
   <>
    <form action={submitAction}>
      [input fields]

      <button type="reset">
        Reset
      </button>
      <CalculateButton />
    </form>

    <Table headers={inputsCols}>
        {result ? (
          Object.entries(result).map(([rowKey, rowData], rowIndex) => (
            <TableRow key={rowIndex}>
              <TableCell key={rowKey} className="font-bold">
                {rowKey}
              </TableCell>

              {Object.entries(rowData).map(([dataKey, dataValue]) => (
                <TableCell key={`${rowKey}-${dataKey}`}>{dataValue}</TableCell>
              ))}
            </TableRow>
          ))
        ) : (
          <TableRow>
            <TableCell colSpan={inputsCols.length}>No data available</TableCell>
          </TableRow>
        )}
      </Table>
    </>
  )
}

function CalculateButton() {
  const { pending } = useFormStatus()

  return (
    <Button
      type="submit"
      color="orange"
      disabled={pending}
      icon={pending ? LoadingIcon : undefined}
    >
      Calculate
    </Button>
  )
}

I am trying to use reacts server action to submit a form and I want to reset

  1. the form state and
  2. the results returned from the server action

when user clicks the reset button.

the first thing working fine with using button type="reset" inside the form but for the second one I am not quite sure how should I do it.

So, How can I reset the state returned from useFormState in react js? to update the UI so user can enter some other data and do other calculations.

Upvotes: 8

Views: 5611

Answers (2)

Merott
Merott

Reputation: 7379

useFormState is now useActionState (from react core) but the API remains the same.

Here's one possible solution, wrapping the original useActionState to provide a reset function:

import {
  useCallback,
  useState,
  useEffect,
  useRef,
  useActionState as useReactActionState,
} from 'react'

export function useActionState<State, Payload>(
  ...args: Parameters<typeof useReactActionState<State, Payload>>
): [
  ...ReturnType<typeof useReactActionState<State, Payload>>,
  resetActionState: () => void,
] {
  const [state, dispatch, isPending] = useReactActionState(...args)
  const [currentState, setCurrentState] = useState(state)

  const currentStateRef = useRef(currentState)
  currentStateRef.current = currentState

  useEffect(() => {
    if (currentStateRef.current !== state) {
      currentStateRef.current = state
      setCurrentState(state)
    }
  }, [state])

  const [, initialState] = args
  const reset = useCallback(() => {
    currentStateRef.current = initialState
    setCurrentState(initialState)
  }, [initialState])

  return [currentState, dispatch, isPending, reset]
}

Bear in mind that this will introduce an additional render cycle because of the setCurrentState call.

You can use it like so:

const [result, submitAction, , resetFormState] = useActionState(submitForm, null)

// ...
// something happened, reset form state
resetFormState()

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 203218

It doesn't appear there is a way to re-initialize the form state from within the component rendering the form. You can, however, use a React key to reset the form from outside.

See Resetting a form with a key.

Use some state to represent a "form key" such that you can provide a new React key and effectively remount the Calculator component which will have the initial state.

export default function Calculator({ onReset }) {
  const [result, submitAction] = useFormState(submitForm, null)

  return (
   <>
    <form action={submitAction} onReset={onReset}>
      ...input fields...

      <button type="reset">
        Reset
      </button>
      <CalculateButton />
    </form>

    ...
    </>
  )
}
const [formKey, setFormKey] = React.useState(() => nanoid());

const updateFormKey = () => setFormKey(nanoid());
...

<Calculator key={formKey} onReset={updateFormKey} />

Basic Demo

const submitForm = (previousState, formData) => {
  console.log({ previousState });
  return previousState + 1;
};

function Calculator({ onReset }) {
  const [result, submitAction] = ReactDOM.useFormState(submitForm, 0);

  return (
    <React.Fragment>
      <form action={submitAction} onReset={onReset}>
        <div>Result: {result}</div>

        <button type="reset">
          Reset
        </button>
        <CalculateButton />
      </form>
    </React.Fragment>
  );
}

function CalculateButton() {
  const { pending } = ReactDOM.useFormStatus()

  return (
    <button
      type="submit"
      disabled={pending}
    >
      Calculate ({pending ? "loading" : "idle" })
    </button>
  );
}

const App = () => {
  const [formKey, setFormKey] = React.useState(0);

  const updateFormKey = () => setFormKey(key => key + 1);
  return <Calculator key={formKey} onReset={updateFormKey} />;
};

const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);

root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.0-canary-cb2439624-20231219/umd/react.development.min.js" integrity="sha512-DyF9mlaay3VPTJNySTYIgb2dsv0NXOcY/IRkCFm/1J/w4B3oOXA6LGwS04cgMFHcSB5b7WXqbOsBaAsWsvcj8g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.0-canary-cb2439624-20231219/umd/react-dom.development.min.js" integrity="sha512-kkJ9iTzcc6cLoFeK+Kp13xvLpIa/+if1NSX7R1ThvHgw6VccDudy8qb5FGyismOvnaGfI604s7ZD6Rzu4Awpng==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="root" />

Upvotes: 4

Related Questions