• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iusehooks / usetheform / 14441018905

14 Apr 2025 08:27AM UTC coverage: 99.21%. Remained the same
14441018905

push

github

antoniopangallo
create workspaces

776 of 793 branches covered (97.86%)

Branch coverage included in aggregate %.

16 of 16 new or added lines in 8 files covered. (100.0%)

1 existing line in 1 file now uncovered.

1610 of 1612 relevant lines covered (99.88%)

1567.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.17
/workspaces/usetheform/src/hooks/useObject.js
1
import { useRef, useEffect, useCallback, useMemo } from "react";
2
import { useOwnContext } from "./useOwnContext";
3
import { useNameProp } from "./commons/useNameProp";
4
import { useMapFields } from "./useMapFields";
5
import { useValidators } from "./useValidators";
6
import { validateProps } from "./../utils/validateProps";
7
import { updateState } from "./../utils/updateState";
8
import { chainReducers } from "./../utils/chainReducers";
9
import { useValidationFunction } from "./commons/useValidationFunction";
10
import { useValidationFunctionAsync } from "./commons/useValidationFunctionAsync";
11
import { STATUS } from "./../utils/constants";
12
import { DISPATCHER_LABEL } from "./../utils/constants";
13
import { noop } from "./../utils/noop";
14

15
const initArray = [];
18✔
16
const initObject = {};
18✔
17
const validatorsDefault = [];
18✔
18

19
export function useObject(props) {
20
  const context = useOwnContext();
4,026✔
21

22
  const {
23
    name,
24
    index,
25
    type,
26
    value: initValue,
27
    reducers,
28
    validators: validatorsFuncs = validatorsDefault,
3,954✔
29
    onValidation = noop,
3,954✔
30
    resetSyncErr = noop,
3,954✔
31
    resetAsyncErr = noop,
3,919✔
32
    asyncValidator,
33
    onAsyncValidation = noop,
3,919✔
34
    touched = false
3,980✔
35
  } = props;
4,026✔
36

37
  const { nameProp, uniqueIDarrayContext, setNameProp, unMountIndex } =
38
    useNameProp(context, name, index);
4,026✔
39

40
  if (process.env.NODE_ENV !== "production") {
4,026!
41
    validateProps(
4,026✔
42
      "<Collection /> ",
43
      { ...props, index: nameProp.current },
44
      context.type
45
    );
46
  }
47

48
  const { unRegisterField, mapFields, updateRegisteredField } = useMapFields(
4,010✔
49
    nameProp,
50
    context,
51
    type
52
  );
53

54
  const applyReducers = useMemo(() => chainReducers(reducers), []);
4,010✔
55

56
  const isMounted = useRef(false);
4,010✔
57
  const stillMounted = useCallback(() => isMounted.current, []);
4,010✔
58

59
  const isArray = type && type === "array";
4,010✔
60
  const init = initValue || (isArray ? initArray : initObject);
4,010✔
61
  const state = useRef(init);
4,010✔
62
  const memoInitialState = useRef(init);
4,010✔
63
  const prevState = useRef(isArray ? initArray : initObject);
4,010✔
64
  const valueFieldLastSyncCheck = useRef(null);
4,010✔
65
  const memoState = useRef(null);
4,010✔
66

67
  // getValue from parent context
68
  if (!isMounted.current) {
4,010✔
69
    state.current = initValue || context.state[nameProp.current] || init;
346✔
70
  } else {
71
    state.current =
3,664✔
72
      context.state[nameProp.current] || (isArray ? initArray : initObject);
4,692✔
73
  }
74

75
  const formState = useRef(null);
4,010✔
76
  formState.current = context.formState;
4,010✔
77

78
  const resetObj = useRef(isArray ? [] : {});
4,010✔
79
  const resetObjKey = useRef({});
4,010✔
80

81
  const registerReset = useCallback(
4,010✔
82
    (namePropExt, fnReset, uniqueIDarrayContext) => {
83
      resetObj.current = isArray
630✔
84
        ? [...resetObj.current]
85
        : { ...resetObj.current };
86

87
      if (isArray && typeof resetObj.current[namePropExt] !== "undefined") {
630✔
88
        if (typeof resetObjKey.current[uniqueIDarrayContext] !== "undefined") {
36!
UNCOV
89
          resetObj.current[namePropExt] = fnReset;
×
90
        } else {
91
          resetObj.current.splice(Number(namePropExt), 0, fnReset);
36✔
92
        }
93
      } else {
94
        resetObj.current[namePropExt] = fnReset;
594✔
95
      }
96

97
      if (typeof uniqueIDarrayContext !== "undefined")
630!
98
        resetObjKey.current[uniqueIDarrayContext] = namePropExt;
630✔
99
    },
100
    []
101
  );
102

103
  const unRegisterReset = useCallback((namePropExt, uniqueIDarrayContext) => {
4,010✔
104
    delete resetObjKey.current[uniqueIDarrayContext];
343✔
105
    if (resetObj.current.constructor === Array) {
343✔
106
      resetObj.current.splice(namePropExt, 1);
235✔
107
    } else {
108
      delete resetObj.current[namePropExt];
108✔
109
    }
110
  }, []);
111

112
  const reset = useCallback(formState => {
4,010✔
113
    valueFieldLastSyncCheck.current = null;
100✔
114
    const initAcc = isArray ? [] : {};
100✔
115
    let obj = Object.keys(resetObj.current).reduce((acc, key) => {
100✔
116
      const value = resetObj.current[key](formState);
207✔
117
      if (value !== undefined) acc[key] = value;
207✔
118
      return acc;
207✔
119
    }, initAcc);
120
    let newValue = applyReducers(obj, memoInitialState.current, formState);
100✔
121
    newValue =
100✔
122
      newValue !== undefined && Object.keys(newValue).length > 0
300✔
123
        ? newValue
124
        : undefined;
125

126
    return newValue;
100✔
127
  }, []);
128

129
  const updateParentProps = useCallback(nextState => {
4,010✔
130
    const newState = applyReducers(nextState, state.current, formState.current);
345✔
131
    const removeProp = Object.keys(newState).length === 0;
345✔
132
    context.changeProp(nameProp.current, newState, removeProp);
345✔
133
  }, []);
134

135
  const changeProp = useCallback((namePropExt, value, removeMe) => {
4,010✔
136
    const nextState = updateState(state.current, {
337✔
137
      value,
138
      nameProp: namePropExt,
139
      removeMe
140
    });
141
    updateParentProps(nextState);
337✔
142
  }, []);
143

144
  const initProp = useCallback(
4,010✔
145
    (namePropExt, value, intialValue, add = true) => {
411✔
146
      const newState = updateState(state.current, {
509✔
147
        value,
148
        nameProp: namePropExt,
149
        add: isMounted.current && add
735✔
150
      });
151

152
      memoInitialState.current = updateState(memoInitialState.current, {
509✔
153
        value: intialValue,
154
        nameProp: namePropExt,
155
        add: isMounted.current && add
735✔
156
      });
157

158
      const reducedState = applyReducers(
509✔
159
        newState,
160
        state.current,
161
        formState.current
162
      );
163

164
      prevState.current = newState;
509✔
165

166
      if (isMounted.current) {
509✔
167
        context.initProp(
226✔
168
          nameProp.current,
169
          reducedState,
170
          memoInitialState.current,
171
          false
172
        );
173
      }
174
      state.current = reducedState;
509✔
175
    },
176
    []
177
  );
178

179
  const { current: removeProp } = useRef(
4,010✔
180
    (
181
      namePropExt,
182
      { currentState, removeCurrent, initialState, removeInitial },
183
      willUnmount = false
405✔
184
    ) => {
185
      let newStateCurrent = updateState(state.current, {
747✔
186
        value: currentState,
187
        nameProp: namePropExt,
188
        removeMe: removeCurrent
189
      });
190

191
      let newStateInitial = updateState(memoInitialState.current, {
747✔
192
        value: initialState,
193
        nameProp: namePropExt,
194
        removeMe: removeInitial
195
      });
196

197
      if (willUnmount && isArray) {
747✔
198
        newStateCurrent = newStateCurrent.filter(
234✔
199
          (elm, index) => index !== namePropExt
302✔
200
        );
201
        newStateInitial = newStateInitial.filter(
234✔
202
          (elm, index) => index !== namePropExt
207✔
203
        );
204
      }
205

206
      const reducedState = applyReducers(
747✔
207
        newStateCurrent,
208
        state.current,
209
        formState.current
210
      );
211

212
      state.current = reducedState;
747✔
213
      memoInitialState.current = newStateInitial;
747✔
214

215
      const removeCurrentProp = Object.keys(state.current).length === 0;
747✔
216

217
      const removeInitialProp =
218
        Object.keys(memoInitialState.current).length === 0;
747✔
219

220
      context.removeProp(nameProp.current, {
747✔
221
        currentState: state.current,
222
        removeCurrent: removeCurrentProp,
223
        initialState: memoInitialState.current,
224
        removeInitial: removeInitialProp
225
      });
226
    }
227
  );
228

229
  const setValue = useCallback(resolveNextState => {
4,010✔
230
    const nextState =
231
      typeof resolveNextState === "function"
8✔
232
        ? resolveNextState(state.current)
233
        : resolveNextState;
234
    updateParentProps(nextState);
8✔
235
  }, []);
236

237
  const [validators, addValidators, removeValidators] = useValidators(
4,010✔
238
    context,
239
    nameProp,
240
    isMounted
241
  );
242

243
  const [
244
    validatorsAsync,
245
    addValidatorsAsync,
246
    removeValidatorsAsync,
247
    validatorsMapsAsync,
248
    updateValidatorsMap
249
  ] = useValidators(context, nameProp, isMounted, true);
4,010✔
250

251
  const { validationMsg, validationObj, validationFN } =
252
    useValidationFunction(validatorsFuncs);
4,010✔
253

254
  const [validationFNAsync] = useValidationFunctionAsync(
4,010✔
255
    asyncValidator,
256
    onAsyncValidation
257
  );
258

259
  const triggerSyncValidation = useCallback(
4,010✔
260
    (propagate = true, onSubmit = false) => {
100✔
261
      if (valueFieldLastSyncCheck.current !== state.current) {
51!
262
        if (validationObj.current !== null && (touched || onSubmit)) {
51✔
263
          valueFieldLastSyncCheck.current = state.current;
5✔
264
          const { isValid, checks } = validationObj.current;
5✔
265
          onValidation(checks, isValid);
5✔
266
        }
267
        if (propagate) {
51✔
268
          context?.triggerSyncValidation?.();
50✔
269
        }
270
      }
271
    },
272
    []
273
  );
274

275
  useEffect(() => {
4,010✔
276
    if (context.formStatus === STATUS.ON_RESET) {
1,035✔
277
      resetSyncErr();
100✔
278
      resetAsyncErr();
100✔
279
    } else if (
935✔
280
      context.formStatus !== STATUS.READY &&
1,516✔
281
      context.formStatus !== STATUS.ON_INIT_ASYNC
282
    ) {
283
      if (
575✔
284
        validationObj.current !== null &&
590✔
285
        context.formStatus === STATUS.ON_SUBMIT
286
      ) {
287
        triggerSyncValidation(false, true);
1✔
288
      }
289

290
      if (validationObj.current !== null && !validationObj.current.isValid) {
575✔
291
        resetAsyncErr();
14✔
292
      }
293
    }
294
  }, [validationMsg.current, context.formStatus]);
295

296
  // used to register async validation Actions
297
  const asyncInitValidation = useRef({});
4,010✔
298
  const registerAsyncInitValidation = useCallback((nameProp, asyncFunc) => {
4,010✔
299
    asyncInitValidation.current[nameProp] = asyncFunc;
14✔
300
  }, []);
301

302
  useEffect(() => {
4,010✔
303
    isMounted.current = true;
330✔
304

305
    if (context.type === "array") {
330✔
306
      context.registerIndex(uniqueIDarrayContext, setNameProp);
130✔
307
    }
308

309
    // Add its own validators
310
    if (validatorsFuncs.length > 0) {
330✔
311
      context.addValidators(nameProp.current, validationFN.current);
10✔
312
    }
313

314
    if (typeof asyncValidator === "function") {
330✔
315
      context.addValidatorsAsync(
7✔
316
        nameProp.current,
317
        validationFNAsync.current,
318
        null
319
      );
320
    }
321

322
    mapFields.current[DISPATCHER_LABEL] = setValue;
330✔
323
    context.updateRegisteredField(nameProp.current, mapFields.current);
330✔
324

325
    // register to parent any initial async Validators to be run ON_INIT
326
    if (Object.keys(asyncInitValidation.current).length > 0) {
330✔
327
      context.registerAsyncInitValidation(
8✔
328
        nameProp.current,
329
        asyncInitValidation.current
330
      );
331
    }
332
    // --- Add its own validators --- //
333

334
    // Add its children validators
335
    if (Object.keys(validators.current).length > 0) {
330✔
336
      context.addValidators(nameProp.current, validators.current);
24✔
337
    }
338

339
    if (Object.keys(validatorsAsync.current).length > 0) {
330✔
340
      context.addValidatorsAsync(
8✔
341
        nameProp.current,
342
        validatorsAsync.current,
343
        validatorsMapsAsync.current
344
      );
345
    }
346
    // --- Add the its children validators --- //
347
    context.registerReset(nameProp.current, reset, uniqueIDarrayContext);
330✔
348

349
    const newState = applyReducers(
330✔
350
      state.current,
351
      prevState.current,
352
      formState.current
353
    );
354

355
    context.initProp(nameProp.current, newState, memoInitialState.current);
330✔
356
    memoState.current = initValue || context.state[nameProp.current] || init;
330✔
357
    return () => {
330✔
358
      resetSyncErr();
330✔
359
      resetAsyncErr();
330✔
360
      isMounted.current = false;
330✔
361
      state.current = memoState.current;
330✔
362

363
      if (context.stillMounted()) {
330✔
364
        context.unRegisterField(nameProp.current);
173✔
365

366
        // remove its own by validators
367
        if (typeof asyncValidator === "function") {
173✔
368
          context.removeValidatorsAsync(
3✔
369
            nameProp.current,
370
            validationFNAsync.current,
371
            false
372
          );
373
        }
374

375
        if (validatorsFuncs.length > 0) {
173✔
376
          context.removeValidators(nameProp.current, validationFN.current);
6✔
377
        }
378
        // ----- remove its own by validators ----- //
379

380
        // remove validators inerithed by children
381
        if (Object.keys(validators.current).length > 0) {
173✔
382
          context.removeValidators(nameProp.current, validators.current);
6✔
383
        }
384

385
        if (Object.keys(validatorsAsync.current).length > 0) {
173✔
386
          context.removeValidatorsAsync(
4✔
387
            nameProp.current,
388
            validatorsAsync.current,
389
            validatorsMapsAsync.current
390
          );
391
        }
392
        // ----- remove validators inerithed by children ----- //
393
        context.removeProp(
173✔
394
          nameProp.current,
395
          {
396
            removeCurrent: true,
397
            removeInitial: true
398
          },
399
          true
400
        );
401

402
        context.unRegisterReset(nameProp.current, uniqueIDarrayContext);
173✔
403
        if (context.type === "array") {
173✔
404
          context.removeIndex(uniqueIDarrayContext);
85✔
405
        }
406
        unMountIndex();
173✔
407
      }
408
    };
409
  }, []);
410

411
  const childrenIndexes = useRef({});
4,010✔
412

413
  const getIndex = useCallback(idCpm => {
4,010✔
414
    if (childrenIndexes.current[idCpm] === undefined) {
344✔
415
      childrenIndexes.current[idCpm] = null;
182✔
416
    }
417
    return Object.keys(childrenIndexes.current).findIndex(v => v == idCpm);
572✔
418
  }, []);
419

420
  const removeIndex = useCallback(idCpm => {
4,010✔
421
    delete childrenIndexes.current[idCpm];
235✔
422
    Object.keys(childrenIndexes.current).forEach((idField, index) =>
235✔
423
      childrenIndexes.current[idField](index)
323✔
424
    );
425
  }, []);
426

427
  const registerIndex = useCallback((idCpm, fn) => {
4,010✔
428
    childrenIndexes.current[idCpm] = fn;
392✔
429
  }, []);
430

431
  return {
4,010✔
432
    state: state.current, // pass the state of the current context down
433
    formState: context.formState, // pass the global form state down
434
    formStatus: context.formStatus, // pass the global form status down
435
    runAsyncValidation: context.runAsyncValidation,
436
    runSyncValidation: context.runSyncValidation,
437
    unRegisterField,
438
    updateRegisteredField,
439
    registerAsyncInitValidation,
440
    changeProp,
441
    initProp,
442
    removeProp,
443
    stillMounted,
444
    getIndex,
445
    removeIndex,
446
    registerIndex,
447
    type,
448
    addValidators,
449
    removeValidators,
450
    addValidatorsAsync,
451
    removeValidatorsAsync,
452
    updateValidatorsMap,
453
    registerReset,
454
    unRegisterReset,
455
    triggerSyncValidation
456
  };
457
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc