Skip to content

Commit

Permalink
feat: improve field array usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Schleemann committed Feb 4, 2024
1 parent 3084bce commit 7512c9e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 7 deletions.
35 changes: 34 additions & 1 deletion src/formbuilder.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ describe("useFormBuilder", () => {
firstName: string;
lastName: string;
};
list: {
id: string;
action: string;
}[];
}

const createHarness = (
Expand Down Expand Up @@ -59,6 +63,10 @@ describe("useFormBuilder", () => {
firstName: "John",
lastName: "Smith",
},
list: [
{ id: "0", action: "frobnicate" },
{ id: "1", action: "skedaddle" },
]
};

beforeAll(() => {
Expand Down Expand Up @@ -151,7 +159,7 @@ describe("useFormBuilder", () => {

await waitFor(() => {
expect(watchedRoot).toHaveTextContent(
JSON.stringify({ person: { firstName: "Joe", lastName: "Smith" } })
JSON.stringify({...defaultValues, person: {...defaultValues.person, firstName: "Joe"}})
);
expect(watchedRoot).toHaveTextContent("Smith");
expect(watchedFirstName).toHaveTextContent("Joe");
Expand Down Expand Up @@ -218,4 +226,29 @@ describe("useFormBuilder", () => {
expect(errorType).toHaveTextContent("required");
});
});

test("$useFieldArray", async () => {
const harness = createHarness({ defaultValues }, (builder) => {
const { fields } = builder.fields.list.$useFieldArray();

return <div>
{
fields.map((field, i) => (
<input
key={field.$key}
{...field.action()}
aria-label={`action-${i}`}
/>
))
}
</div>
});

render(<harness.Form />);

await waitFor(() => {
expect(screen.getByLabelText("action-0")).toHaveValue("frobnicate");
expect(screen.getByLabelText("action-1")).toHaveValue("skedaddle");
});
})
});
25 changes: 19 additions & 6 deletions src/formbuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ type FormBuilderRegisterFn<T> = {
*/
export function createFormBuilder<TFieldValues extends FieldValues>(
methods: UseFormReturn<TFieldValues>,
path: string[]
path: string[],
// Set if created in $useFieldArray()
key?: string,
): FormBuilder<TFieldValues> {
const currentPath = path.join(".") as FieldPath<TFieldValues>;
// Cache generated functions to stabilize references across re-renders.
Expand All @@ -158,14 +160,23 @@ export function createFormBuilder<TFieldValues extends FieldValues>(
// Called when used with `String(...)`.
useCached = () => currentPath;
break;
case "$key":
return key ?? currentPath;
case "$useFieldArray":
useCached = (props?: $UseFieldArrayProps<never>) =>
useFieldArray({
useCached = (props?: $UseFieldArrayProps<never>) => {
const { fields, ...rest } = useFieldArray({
name: currentPath as FieldArrayPath<TFieldValues>,
keyName: "key" as const,
keyName: "$key" as const,
control,
...props,
});
return {
fields: fields.map(
({ $key }, i) => createFormBuilder(methods, [...path, i.toString()], $key)
),
...rest
};
}
break;
case "$useController":
useCached = (
Expand Down Expand Up @@ -329,10 +340,12 @@ interface $UseFieldArrayProps<T> {
shouldUnregister?: boolean;
}

type $UseFieldArrayReturn<T> = UseFieldArrayReturn<
type $UseFieldArrayReturn<T> = Omit<UseFieldArrayReturn<
{ __: T[] },
T extends Primitive | BrowserNativeObject ? never : "__"
>;
>, "fields"> & {
fields: (FormBuilder<T> & {$key: string})[];
};

export type UseFormBuilderProps<
TFieldValues extends FieldValues = FieldValues,
Expand Down

0 comments on commit 7512c9e

Please sign in to comment.