Skip to content

Commit

Permalink
Add useFormContext and defaultValue for field array
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilag committed Oct 7, 2021
1 parent b7eee73 commit 03fdf97
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 13 deletions.
14 changes: 9 additions & 5 deletions example/forms/SimpleFieldArray.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ function SimpleFieldArray(props) {
desc: 'Desc',
},
amount: 1000,
date: "2019-05-12"
date: '2019-05-12',
},
],
},
});
return (
<React.Fragment>
<form onSubmit={handleSubmit}>
<InputField name="name" type="text" />
<InputField
name="name"
type="text"
validate={(val) => (!val ? 'Required' : null)}
/>
<TableField
name="items"
fields={[
Expand All @@ -35,13 +39,13 @@ function SimpleFieldArray(props) {
/>
<WatchField
name="totalAmount"
fieldArrayName='items'
fieldArrayName="items"
colNames={['amount']}
calculateFunc={values =>
calculateFunc={(values) =>
values.reduce((acc, val) => acc + (val?.amount ?? 0), 0)
}
/>
<br/>
<br />
<div>
<button type="submit">Submit</button>
<button type="button" onClick={() => resetInitialValues()}>
Expand Down
93 changes: 88 additions & 5 deletions src/FormProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
resetFieldArrayRow,
setFieldArrayDataAndExtraInfo,
} from './atoms';
import { generateFormId } from './atomUtils';
import { generateFormId, snapshotToGet } from './atomUtils';
import {
IFieldArrayAtomValue,
IFieldArrayColWatchParams,
Expand All @@ -38,6 +38,7 @@ import {
IFieldError,
IFieldProps,
IFieldWatchParams,
IFormContextFieldInput,
InitialValues,
} from './types';
import { getPathInObj, setPathInObj, isDeepEqual, isUndefined } from './utils';
Expand Down Expand Up @@ -239,10 +240,91 @@ export function useFormValues() {
return formValues;
}

// DEVNOTE: useFieldArray will be triggerred only if no. of rows change
// TODO: Need to add support for unregister for unmount (has to be a user choice)
export function useFormContext() {
const setValue = useRecoilCallback(
({ set, snapshot, reset }) =>
(
key: IFormContextFieldInput,
newValue: { value?: any; extraInfo?: any }
) => {
const get = snapshotToGet(snapshot);
const formId = getFormId(get);
const newAtomData = {} as Partial<IFieldAtomValue>;
if (newValue.value) {
newAtomData.data = newValue.value;
}
if (newValue.extraInfo) {
newAtomData.extraInfo = newValue.extraInfo;
}
if (key.type === 'field') {
set(fieldAtomFamily({ ...key, formId }), (atomValue) =>
Object.assign({}, atomValue, newAtomData)
);
} else if (key.type === 'field-array') {
setFieldArrayDataAndExtraInfo(
{
ancestors: key.ancestors,
name: key.name,
},
{
get,
set,
reset,
dataArr: newValue.value,
extraInfoArr: newValue.extraInfo,
}
);
}
},
[]
);

const getValue = useRecoilCallback(
({ snapshot }) =>
(key: IFormContextFieldInput) => {
const get = snapshotToGet(snapshot);
const formId = getFormId(get);
if (key.type === 'field') {
const fieldAtom = get(
fieldAtomFamily({ ...key, formId })
) as IFieldAtomValue;
return { value: fieldAtom.data, extraInfo: fieldAtom.extraInfo };
} else if (key.type === 'field-array') {
const { data, extraInfo } = getFieldArrayDataAndExtraInfo(
{
ancestors: key.ancestors,
name: key.name,
},
get
);
return { value: data, extraInfo };
}
return null;
},
[]
);

const getValues = useRecoilCallback<any, any>(
({ snapshot }) =>
() => {
const get = snapshotToGet(snapshot);
return getFormValues(get);
},
[]
);

return { getValue, setValue, getValues };
}

export function useFieldArray(props: IFieldArrayProps) {
const { name, fieldNames, validate, skipUnregister, ancestors } = props;
const {
name,
fieldNames,
validate,
skipUnregister,
ancestors,
defaultValue,
} = props;
const initialValues = useRecoilValue(formInitialValuesAtom);
const fieldArrayProps = useRecoilValue(
fieldAtomFamily({
Expand Down Expand Up @@ -412,7 +494,8 @@ export function useFieldArray(props: IFieldArrayProps) {
({ set, get, reset }) =>
() => {
const initialValues = get(formInitialValuesAtom);
const initialValue = getPathInObj(initialValues.values, name);
const initialValue =
getPathInObj(initialValues.values, name) ?? defaultValue;
const extraInfo = getPathInObj(initialValues.extraInfos, name);
set(
fieldAtomFamily({
Expand Down
6 changes: 6 additions & 0 deletions src/atomUtils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { RecoilValue, Snapshot } from 'recoil';

export function gan(atomName: string) {
return `WitForm_${atomName}`;
}
Expand All @@ -16,3 +18,7 @@ export function generateFormId() {
Math.random().toString(36).substring(2, 15)
);
}

export function snapshotToGet(snapshot: Snapshot) {
return (atom: RecoilValue<any>) => snapshot.getLoadable(atom).contents;
}
10 changes: 7 additions & 3 deletions src/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
IGetFieldArrayInput,
InitialValues,
} from './types';
import { getPathInObj, setPathInObj } from './utils';
import { getPathInObj, isUndefined, setPathInObj } from './utils';

export const formValuesAtom = atom<FinalValues>({
key: gan('FormValues'),
Expand Down Expand Up @@ -290,8 +290,12 @@ export function getFieldArrayDataAndExtraInfo(
if (fieldErrors?.length) {
errors.push(...fieldErrors);
}
setPathInObj(data[rowIdx], field.name, fieldData);
setPathInObj(extraInfo[rowIdx], field.name, fieldExtraInfo);
if (!isUndefined(fieldData)) {
setPathInObj(data[rowIdx], field.name, fieldData);
}
if (!isUndefined(fieldExtraInfo)) {
setPathInObj(extraInfo[rowIdx], field.name, fieldExtraInfo);
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export interface IFieldArrayProps {
depFields?: string[];
skipUnregister?: boolean;
ancestors?: IAncestorInput[];
defaultValue?: any[];
}

export interface IFieldAtomInput {
Expand All @@ -96,6 +97,10 @@ export interface IFieldArrayRowInput extends IFieldAtomInput {
rowId: number;
}

export interface IFormContextFieldInput extends IFieldAtomInput {
type: IFieldType;
}

export type IFieldAtomSelectorInput = {
ancestors: { name: string; rowId: number }[];
name: string;
Expand Down

0 comments on commit 03fdf97

Please sign in to comment.