Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
fix(useFieldArray): nested field array not working
Browse files Browse the repository at this point in the history
  • Loading branch information
wellyshen committed Jul 28, 2021
1 parent e3eb173 commit df27ef6
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/six-cherries-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-cool-form": patch
---

fix(useFieldArray): nested field array not working
97 changes: 83 additions & 14 deletions app/src/Playground/index.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,92 @@
/* eslint-disable no-console */

import { useForm } from "react-cool-form";
import { useForm, useFieldArray } from "react-cool-form";

export default () => {
const { form, runValidation } = useForm({
// validate: () => ({ foo: "Required" }),
focusOnError: ["foo"],
const { form } = useForm({
defaultValues: {
foo: [
{
name: "Iron Man",
arr: [{ name: "iron arr.0" }, { name: "iron arr.1" }],
},
],
},
onSubmit: (values) => alert(JSON.stringify(values, undefined, 2)),
});
const [fields, { push, insert, remove }] = useFieldArray("foo");

return (
<>
<form ref={form} noValidate>
<input name="foo" required />
<input name="bar" required />
{/* <input type="submit" /> */}
</form>
<button type="button" onClick={() => runValidation(["bar"])}>
Validate
</button>
</>
<form ref={form}>
<table>
<thead>
<tr>
<th>Name</th>
<th>Arr</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{fields.map((fieldName, index) => (
<tr key={fieldName}>
<td>
<input name={`${fieldName}.name`} />
</td>
<td>
<Arr field={fieldName} />
</td>
<td>
<button type="button" onClick={() => remove(index)}>
REMOVE
</button>
</td>
</tr>
))}
</tbody>
</table>
<div>
<button
type="button"
onClick={() => {
push({ name: "Loki", arr: [{ name: "Your Savior Is Here" }] });
}}
>
PUSH
</button>
<button
type="button"
onClick={() =>
insert(0, {
name: "Spider Man",
arr: [{ name: "Your Friendly Neighborhood Spider-Man" }],
})
}
>
INSERT
</button>
</div>
<input type="submit" />
<input type="reset" />
</form>
);
};

function Arr({ field }: any) {
const [fields, { push }] = useFieldArray(`${field}.arr`);

return (
<div>
{fields.map((fieldName) => (
<input
key={fieldName}
style={{ height: "20px", width: "100px" }}
type="text"
name={`${fieldName}.name`}
/>
))}
<button type="button" onClick={() => push({ name: "xxx" })}>
inner push
</button>
</div>
);
}
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export type Fields = Map<

export type Parsers = ObjMap<Omit<FieldOptions, "validate">>;

export type FieldArray = ObjMap<{ fields: ObjMap; reset: () => void }>;
export type FieldArray = ObjMap<{ fields: ObjMap; update: () => void }>;

interface EventOptions<V> {
removeField: RemoveField;
Expand Down
42 changes: 24 additions & 18 deletions src/useFieldArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
get,
getIsDirty,
invariant,
isEmptyObject,
isUndefined,
set,
setValuesAsTrue,
Expand Down Expand Up @@ -66,11 +67,13 @@ export default <T = any, V extends FormValues = FormValues>(
const [fields, setFields] = useState<string[]>(getFields(true));

const updateFields = useCallback(() => {
setFields(getFields());
setNodesOrValues(getState("values"), {
shouldSetValues: false,
fields: Object.keys(fieldArrayRef.current[name].fields),
});
if (fieldArrayRef.current[name]) {
setFields(getFields());
setNodesOrValues(getState("values"), {
shouldSetValues: false,
fields: Object.keys(fieldArrayRef.current[name].fields),
});
}
}, [fieldArrayRef, getFields, getState, name, setNodesOrValues]);

useEffect(() => {
Expand All @@ -82,15 +85,21 @@ export default <T = any, V extends FormValues = FormValues>(
updateFields();
}

const { current: fieldArray } = fieldArrayRef;

return () => {
if (shouldRemoveField(name)) removeField(name);
if (shouldRemoveField(name))
removeField(
name,
isEmptyObject(fieldArray[name].fields) ? ["defaultValue"] : undefined
);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (!fieldArrayRef.current[name])
fieldArrayRef.current[name] = {
reset: updateFields,
update: updateFields,
fields: {},
};
if (validate) fieldValidatorsRef.current[name] = validate;
Expand All @@ -106,8 +115,8 @@ export default <T = any, V extends FormValues = FormValues>(
let state = getState();

(["values", "touched", "errors", "dirty"] as Keys[]).forEach((key) => {
const value = state[key][name];
const fieldsLength = state.values[name]?.length;
const value = get(state[key], name);
const fieldsLength = get(state.values, name)?.length;

if (
key === "values" ||
Expand All @@ -117,15 +126,12 @@ export default <T = any, V extends FormValues = FormValues>(
)
state = set(
state,
key,
{
...state[key],
[name]: handler(
Array.isArray(value) ? [...value] : [],
key,
fieldsLength ? fieldsLength - 1 : 0
),
},
`${key}.${name}`,
handler(
Array.isArray(value) ? [...value] : [],
key,
fieldsLength ? fieldsLength - 1 : 0
),
true
);
});
Expand Down
4 changes: 2 additions & 2 deletions src/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ export default <V extends FormValues = FormValues>({
setNodeValue(name, value);

isFieldArray(fieldArrayRef.current, name, (key) =>
fieldArrayRef.current[key].reset()
fieldArrayRef.current[key].update()
);

if (shouldTouched) setTouched(name, true, { shouldValidate: false });
Expand Down Expand Up @@ -774,7 +774,7 @@ export default <V extends FormValues = FormValues>({
setStateRef("", state);
onResetRef.current(state.values, getOptions(), e);

Object.values(fieldArrayRef.current).forEach((field) => field.reset());
Object.values(fieldArrayRef.current).forEach((field) => field.update());
},
[getOptions, onResetRef, setNodesOrValues, setStateRef, stateRef]
);
Expand Down

0 comments on commit df27ef6

Please sign in to comment.