Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions reports/api-extractor.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ export type Field = {
// @public (undocumented)
export type FieldArray<TFieldValues extends FieldValues = FieldValues, TFieldArrayName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>> = FieldArrayPathValue<TFieldValues, TFieldArrayName> extends ReadonlyArray<infer U> | null | undefined ? U : never;

// @public (undocumented)
export type FieldArrayContextProps<TFieldValues extends FieldValues> = {
fieldArrays?: Record<FieldArrayPath<TFieldValues>, UseFieldArrayReturn<TFieldValues>>;
};

// @public (undocumented)
export type FieldArrayContextReturn<TFieldValues extends FieldValues> = Required<FieldArrayContextProps<TFieldValues>>;

// @public
export type FieldArrayMethodProps = {
shouldFocus?: boolean;
Expand Down Expand Up @@ -273,7 +281,7 @@ export const FormProvider: <TFieldValues extends FieldValues, TContext = any, TT
// @public (undocumented)
export type FormProviderProps<TFieldValues extends FieldValues = FieldValues, TContext = any, TTransformedValues extends FieldValues | undefined = undefined> = {
children: React_2.ReactNode | React_2.ReactNode[];
} & UseFormReturn<TFieldValues, TContext, TTransformedValues>;
} & UseFormReturn<TFieldValues, TContext, TTransformedValues> & FieldArrayContextProps<TFieldValues>;

// @public (undocumented)
export type FormState<TFieldValues extends FieldValues> = {
Expand Down Expand Up @@ -621,7 +629,7 @@ export function useForm<TFieldValues extends FieldValues = FieldValues, TContext
export type UseFormClearErrors<TFieldValues extends FieldValues> = (name?: FieldPath<TFieldValues> | FieldPath<TFieldValues>[] | readonly FieldPath<TFieldValues>[] | `root.${string}` | 'root') => void;

// @public
export const useFormContext: <TFieldValues extends FieldValues, TransformedValues extends FieldValues | undefined = undefined>() => UseFormReturn<TFieldValues>;
export const useFormContext: <TFieldValues extends FieldValues, TransformedValues extends FieldValues | undefined = undefined>() => UseFormReturn<TFieldValues> & Required<FieldArrayContextProps<TFieldValues>>;

// @public
export type UseFormGetFieldState<TFieldValues extends FieldValues> = <TFieldName extends FieldPath<TFieldValues>>(name: TFieldName, formState?: FormState<TFieldValues>) => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/useForm/formState.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ describe('formState', () => {
expect(dirtyFieldsState).toEqual({});
});

describe('when delay config is set', () => {
describe('when delay option is set', () => {
const message = 'required.';

it('should only show error after 500ms with register', async () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/useForm/setValue.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ describe('setValue', () => {
});

describe('with touched', () => {
it('should update touched with shouldTouched config', () => {
it('should update touched with shouldTouched option', () => {
const App = () => {
const {
setValue,
Expand Down
42 changes: 42 additions & 0 deletions src/__tests__/useFormContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';

import { useController } from '../useController';
import { useFieldArray } from '../useFieldArray';
import { useForm } from '../useForm';
import { FormProvider, useFormContext } from '../useFormContext';
import { useFormState } from '../useFormState';
Expand Down Expand Up @@ -229,4 +230,45 @@ describe('FormProvider', () => {

await waitFor(() => screen.getByText('This is required'));
});

it('should work correctly with field array', () => {
type FormValues = {
test: { name: string }[];
};

const Test = () => {
const context = useFormContext<FormValues>();

return (
<>
{context?.fieldArrays?.test.fields.map((field) => (
<input key={field.id} />
))}
</>
);
};

const App = () => {
const methods = useForm();
const testField = useFieldArray({
control: methods.control,
name: 'test',
});

return (
<FormProvider
{...methods}
fieldArrays={{
test: testField,
}}
>
<form>
<Test />
</form>
</FormProvider>
);
};

render(<App />);
});
});
15 changes: 13 additions & 2 deletions src/types/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Subject, Subscription } from '../utils/createSubject';

import { ErrorOption, FieldError, FieldErrors } from './errors';
import { EventType } from './events';
import { FieldArray } from './fieldArray';
import { FieldArray, UseFieldArrayReturn } from './fieldArray';
import {
FieldRefs,
FieldValue,
Expand Down Expand Up @@ -838,10 +838,21 @@ export type UseWatchProps<TFieldValues extends FieldValues = FieldValues> = {
exact?: boolean;
};

export type FieldArrayContextProps<TFieldValues extends FieldValues> = {
fieldArrays?: Record<
FieldArrayPath<TFieldValues>,
UseFieldArrayReturn<TFieldValues>
>;
};

export type FieldArrayContextReturn<TFieldValues extends FieldValues> =
Required<FieldArrayContextProps<TFieldValues>>;

export type FormProviderProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues extends FieldValues | undefined = undefined,
> = {
children: React.ReactNode | React.ReactNode[];
} & UseFormReturn<TFieldValues, TContext, TTransformedValues>;
} & UseFormReturn<TFieldValues, TContext, TTransformedValues> &
FieldArrayContextProps<TFieldValues>;
2 changes: 1 addition & 1 deletion src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useSubscribe } from './useSubscribe';
* @remarks
* [API](https://react-hook-form.com/api/useform) • [Demo](https://codesandbox.io/s/react-hook-form-get-started-ts-5ksmm) • [Video](https://www.youtube.com/watch?v=RkXv4AXXC_4)
*
* @param props - form configuration and validation parameters.
* @param props - form options and validation parameters.
*
* @returns methods - individual functions to manage the form state. {@link UseFormReturn}
*
Expand Down
20 changes: 16 additions & 4 deletions src/useFormContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import React from 'react';

import { FieldValues, FormProviderProps, UseFormReturn } from './types';
import {
FieldArrayContextReturn,
FieldValues,
FormProviderProps,
UseFormReturn,
} from './types';

const HookFormContext = React.createContext<UseFormReturn | null>(null);

Expand Down Expand Up @@ -36,12 +41,15 @@ const HookFormContext = React.createContext<UseFormReturn | null>(null);
*/
export const useFormContext = <
TFieldValues extends FieldValues,
// TODO: add missing a TContext type. We provide TransformedValues type as TContext to UseFormReturn, which is not correct.
TransformedValues extends FieldValues | undefined = undefined,
>(): UseFormReturn<TFieldValues> =>
>(): UseFormReturn<TFieldValues> & FieldArrayContextReturn<TFieldValues> =>
// TODO: we can probably get rid of the "as" cast here by providing a correct Context type to createContext
React.useContext(HookFormContext) as UseFormReturn<
TFieldValues,
TransformedValues
>;
> &
FieldArrayContextReturn<TFieldValues>;

/**
* A provider component that propagates the `useForm` methods to all children components via [React Context](https://reactjs.org/docs/context.html) API. To be used with {@link useFormContext}.
Expand Down Expand Up @@ -82,7 +90,11 @@ export const FormProvider = <
) => {
const { children, ...data } = props;
return (
<HookFormContext.Provider value={data as unknown as UseFormReturn}>
<HookFormContext.Provider
value={
data as unknown as UseFormReturn & FieldArrayContextReturn<TFieldValues>
}
>
{children}
</HookFormContext.Provider>
);
Expand Down