Skip to content

Commit 9dd2307

Browse files
committed
feat: add flattenObjectKeys, serialize stores, sids
1 parent 4395445 commit 9dd2307

9 files changed

Lines changed: 83 additions & 14 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ interface Form {
119119
// Keep form data on unmount
120120
// Default: false
121121
keepOnUnmount: boolean;
122+
// PROPERTY - serialize - serialize stores
123+
// not reactive, initialized only once on form creation
124+
// Default: false
125+
serialize?: boolean;
122126
// Set fields validation behavior onBlur
123127
// Default: true
124128
validateOnBlur?: boolean;
@@ -501,6 +505,7 @@ import {
501505
shapeFy,
502506
truthyFyStore,
503507
shapeFyStore,
508+
flattenObjectKeys,
504509
} from 'efx-forms/utils';
505510

506511
/**
@@ -528,6 +533,13 @@ const $truthyStore = truthyFyStore($values);
528533
* @type ($values: Store): Store => $shapedValues
529534
*/
530535
const $shapedStore = shapeFyStore($values);
536+
537+
/**
538+
* Return flatten one level object keys/values
539+
* helper for the nested initial values
540+
* @type (values: Record<string, any>): Record<string, any> => ({})
541+
*/
542+
const initialValues = flattenObjectKeys(values);
531543
```
532544

533545
# Validators

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "efx-forms",
3-
"version": "2.0.26",
3+
"version": "2.0.27",
44
"description": "Effector JS Forms",
55
"main": "index.js",
66
"types": "index.d.ts",

src/FormComponent.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { FormEvent } from 'react';
2-
import { useEffect } from 'react';
3-
import React, { useMemo } from 'react';
2+
import React, { useMemo, useEffect } from 'react';
43
import { useUnit } from 'effector-react';
54
import pickBy from 'lodash/pickBy';
65
import isEmpty from 'lodash/isEmpty';
@@ -24,6 +23,7 @@ export const Form = ({
2423
validateOnChange,
2524
validators,
2625
disableFieldsReinit,
26+
serialize,
2727
...props
2828
}: IRFormProps) => {
2929
const form: IForm = useMemo(() => {
@@ -36,6 +36,7 @@ export const Form = ({
3636
validateOnChange,
3737
validators,
3838
disableFieldsReinit,
39+
serialize,
3940
},
4041
(val) => val !== undefined,
4142
);
@@ -54,6 +55,7 @@ export const Form = ({
5455
useEffect(() => {
5556
const config = pickBy(
5657
{
58+
serialize,
5759
validators,
5860
initialValues,
5961
keepOnUnmount,
@@ -73,6 +75,7 @@ export const Form = ({
7375
initialValues,
7476
keepOnUnmount,
7577
validators,
78+
serialize,
7679
name,
7780
form,
7881
]);

src/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const FORM_CONFIG: IFormConfig = {
1111
validateOnChange: false,
1212
disableFieldsReinit: false,
1313
validators: {},
14+
serialize: false,
1415
};
1516

1617
export const FIELD_CONFIG: IFieldConfig = {
@@ -26,4 +27,4 @@ export const FIELD_CONFIG: IFieldConfig = {
2627
export const ARR_0 = Object.freeze([]);
2728
export const OBJ_0 = Object.freeze({});
2829

29-
export const $null = domain.store<any>(null, { name: '$null' });
30+
export const $null = domain.store<any>(null, { name: '$null', sid: '$efx-forms-null' });

src/form.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ export const createFormHandler = (formConfig: IFormConfig): IForm => {
5959
* Fields status store - keeps fields active / mounted status
6060
*/
6161
const $active = dm
62-
.store<Record<string, boolean>>({}, { name: '$active' })
62+
.store<Record<string, boolean>>({}, {
63+
name: '$active',
64+
serialize: formConfig.serialize ? undefined : 'ignore',
65+
sid: `efx-forms-${formConfig.name}-$active`,
66+
})
6367
.on(setActive, (state, { name, value }) =>
6468
state[name] !== value ? Object.assign({}, state, { [name]: value }) : state,
6569
)
@@ -69,7 +73,11 @@ export const createFormHandler = (formConfig: IFormConfig): IForm => {
6973
* Values store - fields values
7074
*/
7175
const $values = dm
72-
.store<Record<string, any>>({}, { name: '$values' })
76+
.store<Record<string, any>>({}, {
77+
name: '$values',
78+
serialize: formConfig.serialize ? undefined : 'ignore',
79+
sid: `efx-forms-${formConfig.name}-$values`,
80+
})
7381
.on(setValues, (state, values) => Object.assign({}, state, values))
7482
.on(onChange, (state, { name, value }) => {
7583
const parse = data.configs[name]?.parse || FIELD_CONFIG.parse!;
@@ -109,7 +117,11 @@ export const createFormHandler = (formConfig: IFormConfig): IForm => {
109117
* Validations store - keeps all fields validation errors
110118
*/
111119
const $errors = dm
112-
.store<Record<string, string[] | null>>({}, { name: '$errors' })
120+
.store<Record<string, string[] | null>>({}, {
121+
name: '$errors',
122+
serialize: formConfig.serialize ? undefined : 'ignore',
123+
sid: `efx-forms-${formConfig.name}-$errors`,
124+
})
113125
.on(setError, (state, { name, errors }) =>
114126
Object.assign({}, state, { [name]: errors }),
115127
)
@@ -198,7 +210,11 @@ export const createFormHandler = (formConfig: IFormConfig): IForm => {
198210
* Touches store - keeps all fields touch state
199211
*/
200212
const $touches = dm
201-
.store<Record<string, boolean>>({}, { name: '$touches' })
213+
.store<Record<string, boolean>>({}, {
214+
name: '$touches',
215+
serialize: formConfig.serialize ? undefined : 'ignore',
216+
sid: `efx-forms-${formConfig.name}-$touches`,
217+
})
202218
.on(onChange, (state, { name }) =>
203219
state[name] ? state : Object.assign({}, state, { [name]: true }),
204220
)
@@ -225,7 +241,11 @@ export const createFormHandler = (formConfig: IFormConfig): IForm => {
225241
* Dirties store - keeps all fields dirty state
226242
*/
227243
const $dirties = dm
228-
.store<Record<string, boolean>>({}, { name: '$dirties' })
244+
.store<Record<string, boolean>>({}, {
245+
name: '$dirties',
246+
serialize: formConfig.serialize ? undefined : 'ignore',
247+
sid: `efx-forms-${formConfig.name}-$dirties`,
248+
})
229249
.on(onChange, (state, { name, value }) => {
230250
const dirty = value !== getFieldInitVal(data, name);
231251
return state[name] === dirty ? state : Object.assign({}, state, { [name]: dirty });

src/tests/Update.spec.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ test('Form Update', async ({ mount }) => {
4747
keepOnUnmount: false,
4848
name: 'formUpdate',
4949
onSubmit: undefined,
50+
serialize: false,
5051
skipClientValidation: false,
5152
validateOnBlur: true,
5253
validateOnChange: false,
@@ -100,6 +101,7 @@ test('Form Update', async ({ mount }) => {
100101
keepOnUnmount: true,
101102
name: 'formUpdate',
102103
onSubmit: undefined,
104+
serialize: false,
103105
skipClientValidation: true,
104106
validateOnBlur: false,
105107
validateOnChange: true,
@@ -153,6 +155,7 @@ test('Form Update', async ({ mount }) => {
153155
keepOnUnmount: false,
154156
name: 'formUpdate',
155157
onSubmit: undefined,
158+
serialize: false,
156159
skipClientValidation: false,
157160
validateOnBlur: true,
158161
validateOnChange: false,

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ export interface IFormConfig {
8484
validateOnChange?: boolean;
8585
/** PROPERTY - keepOnUnmount - keep form data on form unmount */
8686
keepOnUnmount?: boolean;
87+
/** PROPERTY - serialize forms stores, default false */
88+
serialize?: boolean;
8789
/** PROPERTY - skipClientValidation - if true will skip validation on submit */
8890
skipClientValidation?: boolean;
8991
/**

src/utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import type { Domain, Store } from 'effector';
33
import reduce from 'lodash/reduce';
44
import set from 'lodash/set';
55
import pickBy from 'lodash/pickBy';
6+
import forEach from 'lodash/forEach';
7+
import isArray from 'lodash/isArray';
8+
import isPlainObject from 'lodash/isPlainObject';
69

710
export const domain: Domain = createDomain('@fx-forms');
811

@@ -34,3 +37,28 @@ export const shapeFyStore = ($store: Store<TObject>) => $store.map(shapeFy);
3437
*/
3538
export const hasTruthy = (obj: Record<string, any>) =>
3639
Object.values(obj).some(Boolean);
40+
41+
/**
42+
* Transform object to flat, one level, object
43+
*/
44+
export const flattenObjectKeys = (obj = {}) => {
45+
const result = {};
46+
47+
const flatten = (collection: Record<string, any>, prefix = '', suffix = '') => {
48+
forEach(collection, (value, key) => {
49+
const path = `${prefix}${key}${suffix}`;
50+
51+
if (isArray(value)) {
52+
flatten(value, `${path}[`, ']');
53+
} else if (isPlainObject(value)) {
54+
flatten(value, `${path}.`);
55+
} else {
56+
result[path] = value;
57+
}
58+
});
59+
};
60+
61+
flatten(obj);
62+
63+
return result;
64+
};

0 commit comments

Comments
 (0)