I'm using react-hook-form and react-navigation. In my use case, I'm using useFieldArray to control a growing array of inputs and I want to reset the form when the user press the HeaderBackButton. But when I pass reset to the onPress prop of HeaderBackButton and press the button, react-hook-form errors with Cannot read properties of undefined (reading 'id') whenever there is any change to the field array.
In the codebox below, you can append an item, and click Reset and reset the form. But clicking Back after appending an item would produce the error. This problem only appears in react-hook-form < 7.22.0
CodeSandbox: https://codesandbox.io/embed/react-hook-form-reset-usefieldarray-forked-yk7g4?fontsize=14&hidenavigation=1&theme=dark
import React from "react";
import { useForm, useFieldArray, Controller } from "react-hook-form";
import ReactDOM from "react-dom";
import { HeaderBackButton } from '@react-navigation/stack';
import "./styles.css";
let renderCount = 0;
function App() {
const { control, handleSubmit, reset } = useForm({
defaultValues: {
loadState: "unloaded",
names: []
}
});
const { fields, append } = useFieldArray({
control,
name: "names"
});
renderCount++;
return (
<form>
<h1>Field Array </h1>
<p>1. Append using the append button. </p>
<p>2a. reset button works as expected</p>
<p>2b. Back button, which is a HeaderBackButton errors out when there is any change to the field array</p>
<span className="counter">Render Count: {renderCount}</span>
<HeaderBackButton tintColor={'#FFFFFF'}
labelVisible={true}
onPress={()=>{reset()}}
/>
<button type='button' onClick={()=>reset()}>reset</button>
<button type='button' onClick={()=>append('')}>append</button>
<ul>
{fields.map((item, index) => (
<Controller
key={item.id}
render={({ field }) => <input {...field} />}
name={`names[${index}]`}
control={control}
defaultValue={item.lastName}
/>
))}
</ul>
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Verified that react-hook-form 7.22.0 fixes this issue.
But the fix in 7.22.0 only makes all elements in the field array undefined. It does not clear out the field array, which is still causing problems for my specific case.
Author has identified crux of the problem is calling reset asynchronously. An updated sandbox demonstrates that 7.22.2 of the library fixes the problem entirely.
This code works as expected with react-hook-form 7.22.2
import React from "react";
import { useForm, useFieldArray, Controller } from "react-hook-form";
import ReactDOM from "react-dom";
import { HeaderBackButton } from "@react-navigation/stack";
import "./styles.css";
let renderCount = 0;
function App() {
const { getValues, control, reset } = useForm({
defaultValues: {
loadState: "unloaded",
names: []
}
});
const { fields, append } = useFieldArray({
control,
name: "names"
});
renderCount++;
console.log(getValues());
return (
<form>
<h1>Field Array </h1>
<p>1. Append using the append button. </p>
<p>2a. reset button works as expected</p>
<p>
2b. Back button, which is a HeaderBackButton does clear out the
field array now
</p>
<span className="counter">Render Count: {renderCount}</span>
<HeaderBackButton
tintColor={"#FFFFFF"}
labelVisible={true}
onPress={() => reset({ names: [] })}
/>
<button type="button" onClick={() => reset({ names: [] })}>
reset
</button>
<button type="button" onClick={() => append({ name: "abc" })}>
append
</button>
<ul>
{fields.map((item, index) => (
<Controller
key={item.id}
render={({ field }) => <input value={field.value} />}
name={`names.${index}.name`}
control={control}
/>
))}
</ul>
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);