Skip to content

Sync to v7.72.0#99

Merged
bombillazo merged 7 commits into
masterfrom
task/sync-master
Mar 26, 2026
Merged

Sync to v7.72.0#99
bombillazo merged 7 commits into
masterfrom
task/sync-master

Conversation

@bombillazo

Copy link
Copy Markdown
Owner

Resolves #98

Summary

  • Sync with upstream React Hook Form v7.72.0
  • Adds form-level validation (validate option in useForm)
  • New field array dirty fields tests
  • Preserves all RHF+ custom features (metadata, isLoading, id)

bluebill1049 and others added 7 commits February 28, 2026 12:00
* feat: build in form valdiate

* update max size

* update with input react to validate form

* update api

* update api

* fix type

* fix all the format issues

* update valdiation logic and add test

* remove only

* update validation logic to support event-based validation

* make prop name shorter and update validation

* fix tests and api contractor

* rename form error to form

* rename to valid

* minor clean up

* revert back function

* fix with early exit

* fix test

* fix type and contract

* update api

* lint update
…eact-hook-form#13299)

* 🐞 fix: prevent useFieldArray from marking unrelated fields as dirty (react-hook-form#13054)

Previously, _setFieldArray replaced the entire dirtyFields object via
getDirtyFields(), which included false entries for non-dirty fields.
This caused unrelated fields (name, age, etc.) to appear in dirtyFields
after append/remove operations.

Now only the field array portion of dirtyFields is updated, preserving
the dirty state of other fields.

* 🐞 fix: use root field name for nested field array dirty tracking

* 🐞 fix: use getNodeParentName for nested field array dirty tracking

* ✅ test: remove test comments

* ✅ test: add nested indexed field array regression test
This commit updates the react-hook-form V7 JavaScript and TypeScript
CodeSandbox links in GitHub issue template. This will ensure that the
latest version of `react-hook-form` is used.
Copilot AI review requested due to automatic review settings March 26, 2026 23:51
@github-actions

Copy link
Copy Markdown

Size Change: +1.61 kB (+2.36%)

Total Size: 69.9 kB

Filename Size Change
dist/index.cjs.js 13.2 kB +262 B (+2.03%)
dist/index.esm.mjs 26.7 kB +559 B (+2.14%)
dist/index.umd.js 13.2 kB +255 B (+1.96%)
dist/react-server.esm.mjs 16.9 kB +534 B (+3.27%)

compressed-size-action

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Syncs this library’s React Hook Form–compatible API surface to upstream RHF v7.72.0, including new form-level validation and additional formState subscriptions, while adding regression tests around field array dirtyFields isolation.

Changes:

  • Introduces form-level validation via a new validate option in useForm and new errors.form/form.* error paths.
  • Extends subscribable formState fields to include isSubmitted and submitCount.
  • Adjusts dirtyFields updates for field array operations and adds/expands tests to ensure unrelated dirtyFields aren’t polluted.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/types/validator.ts Adds ValidateForm/ValidateFormEventType and form validation result typing.
src/types/form.ts Wires new validate prop into UseFormProps; adds isSubmitted/submitCount to ReadFormState; expands setError/clearErrors name types to include form.
src/types/errors.ts Adds errors.form typing.
src/logic/createFormControl.ts Implements form-level validation execution and error mapping; updates dirtyFields handling for field arrays.
src/logic/updateFieldArrayRootError.ts Uses new ROOT_ERROR_TYPE constant for field-array root error key.
src/constants.ts Adds additional event constants and new FORM_ERROR_TYPE/ROOT_ERROR_TYPE constants.
src/tests/** Adds/updates tests for submit-state subscriptions, form-level validation behavior, and dirtyFields isolation.
reports/api-extractor.md.api.md Updates API report; introduces new API Extractor warning around EVENTS.
package.json Bumps package version to 7.72.0.
.github/ISSUE_TEMPLATE/bug_report.yml Adds CodeSandbox template links to the bug report template.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/types/validator.ts
Comment on lines +1 to +5
import type { EVENTS, INPUT_VALIDATION_RULES } from '../constants';

import type { Message } from './errors';
import type { FieldValues } from './fields';
import type { Mode } from './form';
import type { FormState, Mode } from './form';

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidateFormEventType / InputValidationRules use typeof EVENTS / typeof INPUT_VALIDATION_RULES, but those are imported with import type. Type-only imports can’t be referenced in a typeof type query, so this should fail TS compilation. Use a value import (import { EVENTS, INPUT_VALIDATION_RULES }) or refactor the types to not depend on the runtime constants.

Copilot uses AI. Check for mistakes.
Comment thread src/types/errors.ts
Comment on lines 52 to 55
> & {
root?: Record<string, GlobalError> & GlobalError;
form?: GlobalError;
};

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UseFormSetError/UseFormClearErrors now allow 'form' and form.${string}, and createFormControl sets errors at paths like form.${key}. But FieldErrors types form?: GlobalError, which doesn’t support nested keys. Either change form to a nested map type (similar to root?: Record<string, GlobalError> & GlobalError) or remove support for form.${string} so the public types match runtime behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +588 to +590
setError(`${FORM_ERROR_TYPE}.${key}`, {
message: isString(result.message) ? result.message : '',
type: INPUT_VALIDATION_RULES.validate,

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the object-result branch, message: isString(result.message) ? result.message : '' reads from result.message, but result is the whole map (key -> error), so this will always be '' and ignore the per-key error details. It also hardcodes type instead of using the returned error.type. Use the error variable (result[key]) for message/type (and handle when it’s missing).

Suggested change
setError(`${FORM_ERROR_TYPE}.${key}`, {
message: isString(result.message) ? result.message : '',
type: INPUT_VALIDATION_RULES.validate,
const message = isObject(error)
? (isString((error as any).message) ? (error as any).message : '')
: isString(error)
? error
: '';
const type =
isObject(error) && isString((error as any).type)
? (error as any).type
: INPUT_VALIDATION_RULES.validate;
setError(`${FORM_ERROR_TYPE}.${key}`, {
message,
type,

Copilot uses AI. Check for mistakes.
Comment on lines +628 to +641
if (props.validate) {
context.runRootValidation = true;
const result = await validateForm({
name,
eventType,
});

if (!result) {
context.valid = false;

if (onlyCheckValid) {
return context.valid;
}
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

executeBuiltInValidation marks the form invalid only when !result, but props.validate can return a string (error message) or an object (per-field errors), both of which are truthy. That means _formState.isValid can remain true while form-level errors have been set. The validity check should treat string/object-with-errors as invalid (and only true as valid).

Copilot uses AI. Check for mistakes.
Comment on lines +609 to +634
const executeBuiltInValidation = async ({
fields,
onlyCheckValid,
name,
eventType,
context = {
valid: true,
runRootValidation: false,
},
) => {
}: {
fields: FieldRefs;
onlyCheckValid?: boolean;
name?: FieldPath<TFieldValues> | FieldPath<TFieldValues>[];
eventType: ValidateFormEventType;
context?: {
valid: boolean;
runRootValidation?: boolean;
};
}) => {
if (props.validate) {
context.runRootValidation = true;
const result = await validateForm({
name,
eventType,
});

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

executeBuiltInValidation calls validateForm at the start whenever props.validate exists, and then recursively calls itself for nested field objects. Because context.runRootValidation is never checked, form-level validation can run many times during a single validation pass (once per recursion), which is both expensive and can lead to repeated/stale error updates. Consider guarding with if (props.validate && !context.runRootValidation) (or similar) so form-level validation runs only once per pass.

Copilot uses AI. Check for mistakes.
Comment on lines +987 to +990
// Warning: (ae-forgotten-export) The symbol "EVENTS" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ValidateFormEventType = (typeof EVENTS)[keyof typeof EVENTS];

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Extractor is now emitting an (ae-forgotten-export) warning for EVENTS because ValidateFormEventType references it. This usually breaks the API report / CI gate. Either export EVENTS from the public entrypoint (src/index.ts) or change ValidateFormEventType to not depend on the EVENTS value (e.g., a literal union), so the public d.ts can be generated without forgotten-export warnings.

Suggested change
// Warning: (ae-forgotten-export) The symbol "EVENTS" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export type ValidateFormEventType = (typeof EVENTS)[keyof typeof EVENTS];
// @public (undocumented)
export type ValidateFormEventType = string;

Copilot uses AI. Check for mistakes.
Comment on lines +583 to +594
if (isObject(result)) {
for (const key in result) {
const error = result[key];

if (error) {
setError(`${FORM_ERROR_TYPE}.${key}`, {
message: isString(result.message) ? result.message : '',
type: INPUT_VALIDATION_RULES.validate,
});
}
}
} else if (isString(result) || !result) {

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When props.validate returns an object, validateForm only sets errors for keys present/truthy, but it never clears existing form.* errors when the object is empty or when a previously-returned key disappears. This can leave stale form-level errors in state; consider clearing FORM_ERROR_TYPE before applying the new per-key errors (or explicitly unsetting keys not returned).

Copilot uses AI. Check for mistakes.
@bombillazo bombillazo merged commit ba8a280 into master Mar 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

‼️ Sync with v7.72.0

6 participants