React Native TextInput components are always controlled from the native side, but you still want uncontrolled form state on the JS thread. react-hook-form subscribes to field changes instead of re-rendering the whole screen on every keystroke - the pattern that keeps multi-field signup and checkout flows smooth on mid-range Android devices.
Spreading register on TextInput - register returns web-centric props (onChange, name) that do not map to onChangeText. Fix: Use Controller and map onChange → onChangeText.
Missing defaultValues keys - New fields show undefined, isDirty stays false after edits, and reset() skips them. Fix: List every field in defaultValues, including empty strings and empty arrays.
Destructuring all of formState - const { formState } = useForm() then reading formState.isSubmitting in render still works, but const { isSubmitting, errors, isDirty, isValid } = formState subscribes to four signals at once. Fix: Destructure only what the component displays.
watch() in the parent render - watch() without arguments re-renders on every keystroke across all fields - the problem RHF solves. Fix:watch("email") for one field, or move watched UI into an isolated child.
undefined TextInput value - Passing value={undefined} triggers warnings and clears cursor position. Fix:value={value ?? ""} in the Controller render.
onSubmit without handleSubmit - Pressing the button skips validation and sends partial data. Fix:onPress={handleSubmit(onSubmit)} on Pressable, never onPress={onSubmit}.
Server errors not mapped to fields - API returns { fieldErrors: { email: "Taken" } } but the UI shows a generic alert. Fix: Loop setError("email", { type: "server", message }) and setFocus("email").