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
- the form state and
- 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.
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
- the form state and
- 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.
Share Improve this question edited Jan 15, 2024 at 18:37 Aashutosh Kumar asked Jan 14, 2024 at 18:00 Aashutosh KumarAashutosh Kumar 812 silver badges5 bronze badges 1-
1
I like to return a key from my server action, like
key: new Date.now()
, and then use auseEffect
hook. Source: robinwieruch.de/next-forms – Robin Wieruch Commented Mar 18, 2024 at 7:38
2 Answers
Reset to default 4It doesn't appear there is a way to re-initialize the form state from within the ponent 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
ponent 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./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./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" />
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()