diff --git a/README.md b/README.md
index 98e59e6e5..231f859b3 100644
--- a/README.md
+++ b/README.md
@@ -306,81 +306,81 @@ npm install yup --save
** Table of Contents **
-
-
-* [Guides](#guides)
- * [Basics](#basics)
- * [React Native](#react-native)
- * [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange)
- * [Avoiding new functions in render](#avoiding-new-functions-in-render)
- * [Using Formik with TypeScript](#using-formik-with-typescript)
- * [Render props (`` and ``)](#render-props-formik--and-field)
- * [`withFormik()`](#withformik)
-* [API](#api)
- * [``](#formik-)
- * [Formik render methods](#formik-render-methods)
- * [Formik props](#formik-props)
- * [`dirty: boolean`](#dirty-boolean)
- * [`errors: { [field: string]: string }`](#errors--field-string-string-)
- * [`handleBlur: (e: any) => void`](#handleblur-e-any--void)
- * [`handleChange: (e: React.ChangeEvent) => void`](#handlechange-e-reactchangeeventany--void)
- * [`handleReset: () => void`](#handlereset---void)
- * [`handleSubmit: (e: React.FormEvent) => void`](#handlesubmit-e-reactformeventhtmlformevent--void)
- * [`isSubmitting: boolean`](#issubmitting-boolean)
- * [`isValid: boolean`](#isvalid-boolean)
- * [`resetForm: (nextValues?: Values) => void`](#resetform-nextvalues-values--void)
- * [`setErrors: (fields: { [field: string]: string }) => void`](#seterrors-fields--field-string-string---void)
- * [`setFieldError: (field: string, errorMsg: string) => void`](#setfielderror-field-string-errormsg-string--void)
- * [`setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void`](#setfieldtouched-field-string-istouched-boolean-shouldvalidate-boolean--void)
- * [`setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void`](#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void)
- * [`setStatus: (status?: any) => void`](#setstatus-status-any--void)
- * [`setSubmitting: (isSubmitting: boolean) => void`](#setsubmitting-issubmitting-boolean--void)
- * [`setTouched: (fields: { [field: string]: boolean }) => void`](#settouched-fields--field-string-boolean---void)
- * [`setValues: (fields: { [field: string]: any }) => void`](#setvalues-fields--field-string-any---void)
- * [`status?: any`](#status-any)
- * [`touched: { [field: string]: boolean }`](#touched--field-string-boolean-)
- * [`values: { [field: string]: any }`](#values--field-string-any-)
- * [`validateForm: (values?: any) => void`](#validateform-values-any--void)
- * [`component`](#component)
- * [`render: (props: FormikProps) => ReactNode`](#render-props-formikpropsvalues--reactnode)
- * [`children: func`](#children-func)
- * [`enableReinitialize?: boolean`](#enablereinitialize-boolean)
- * [`isInitialValid?: boolean`](#isinitialvalid-boolean)
- * [`initialValues?: Values`](#initialvalues-values)
- * [`onReset?: (values: Values, formikBag: FormikBag) => void`](#onreset-values-values-formikbag-formikbag--void)
- * [`onSubmit: (values: Values, formikBag: FormikBag) => void`](#onsubmit-values-values-formikbag-formikbag--void)
- * [`validate?: (values: Values) => FormikError | Promise`](#validate-values-values--formikerrorvalues--promiseany)
- * [`validateOnBlur?: boolean`](#validateonblur-boolean)
- * [`validateOnChange?: boolean`](#validateonchange-boolean)
- * [`validationSchema?: Schema | (() => Schema)`](#validationschema-schema----schema)
- * [``](#field-)
- * [`validate?: (value: any) => undefined | string | Promise`](#validate-value-any--undefined--string--promiseany)
- * [``](#fieldarray)
- * [`name: string`](#name-string)
- * [FieldArray Helpers](#fieldarray-helpers)
- * [FieldArray render methods](#fieldarray-render-methods)
- * [`render: (arrayHelpers: ArrayHelpers) => React.ReactNode`](#render-arrayhelpers-arrayhelpers--reactreactnode)
- * [`component: React.ReactNode`](#component-reactreactnode)
- * [``](#form-)
- * [`withFormik(options)`](#withformikoptions)
- * [`options`](#options)
- * [`displayName?: string`](#displayname-string)
- * [`enableReinitialize?: boolean`](#enablereinitialize-boolean-1)
- * [`handleSubmit: (values: Values, formikBag: FormikBag) => void`](#handlesubmit-values-values-formikbag-formikbag--void)
- * [The "FormikBag":](#the-formikbag)
- * [`isInitialValid?: boolean | (props: Props) => boolean`](#isinitialvalid-boolean--props-props--boolean)
- * [`mapPropsToValues?: (props: Props) => Values`](#mappropstovalues-props-props--values)
- * [`validate?: (values: Values, props: Props) => FormikError | Promise`](#validate-values-values-props-props--formikerrorvalues--promiseany)
- * [`validateOnBlur?: boolean`](#validateonblur-boolean-1)
- * [`validateOnChange?: boolean`](#validateonchange-boolean-1)
- * [`validationSchema?: Schema | ((props: Props) => Schema)`](#validationschema-schema--props-props--schema)
- * [Injected props and methods](#injected-props-and-methods)
-* [Organizations and projects using Formik](#organizations-and-projects-using-formik)
-* [Authors](#authors)
-* [Contributors](#contributors)
+- [Guides](#guides)
+ - [Basics](#basics)
+ - [React Native](#react-native)
+ - [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange)
+ - [Avoiding new functions in render](#avoiding-new-functions-in-render)
+ - [Using Formik with TypeScript](#using-formik-with-typescript)
+ - [Render props (`` and ``)](#render-props-formik--and-field)
+ - [`withFormik()`](#withformik)
+- [API](#api)
+ - [``](#formik-)
+ - [Formik render methods](#formik-render-methods)
+ - [Formik props](#formik-props)
+ - [`dirty: boolean`](#dirty-boolean)
+ - [`errors: { [field: string]: string }`](#errors--field-string-string-)
+ - [`handleBlur: (e: any) => void`](#handleblur-e-any--void)
+ - [`handleChange: (e: React.ChangeEvent) => void`](#handlechange-e-reactchangeeventany--void)
+ - [`handleReset: () => void`](#handlereset---void)
+ - [`handleSubmit: (e: React.FormEvent) => void`](#handlesubmit-e-reactformeventhtmlformevent--void)
+ - [`isSubmitting: boolean`](#issubmitting-boolean)
+ - [`isValid: boolean`](#isvalid-boolean)
+ - [`resetForm: (nextValues?: Values) => void`](#resetform-nextvalues-values--void)
+ - [`setErrors: (fields: { [field: string]: string }) => void`](#seterrors-fields--field-string-string---void)
+ - [`setFieldError: (field: string, errorMsg: string) => void`](#setfielderror-field-string-errormsg-string--void)
+ - [`setFieldTouched: (field: string, isTouched: boolean, shouldValidate?: boolean) => void`](#setfieldtouched-field-string-istouched-boolean-shouldvalidate-boolean--void)
+ - [`setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void`](#setfieldvalue-field-string-value-any-shouldvalidate-boolean--void)
+ - [`setStatus: (status?: any) => void`](#setstatus-status-any--void)
+ - [`setSubmitting: (isSubmitting: boolean) => void`](#setsubmitting-issubmitting-boolean--void)
+ - [`setTouched: (fields: { [field: string]: boolean }) => void`](#settouched-fields--field-string-boolean---void)
+ - [`setValues: (fields: { [field: string]: any }) => void`](#setvalues-fields--field-string-any---void)
+ - [`status?: any`](#status-any)
+ - [`touched: { [field: string]: boolean }`](#touched--field-string-boolean-)
+ - [`values: { [field: string]: any }`](#values--field-string-any-)
+ - [`validateForm: (values?: any) => void`](#validateform-values-any--void)
+ - [`component`](#component)
+ - [`render: (props: FormikProps) => ReactNode`](#render-props-formikpropsvalues--reactnode)
+ - [`children: func`](#children-func)
+ - [`enableReinitialize?: boolean`](#enablereinitialize-boolean)
+ - [`isInitialValid?: boolean`](#isinitialvalid-boolean)
+ - [`initialValues?: Values`](#initialvalues-values)
+ - [`onReset?: (values: Values, formikBag: FormikBag) => void`](#onreset-values-values-formikbag-formikbag--void)
+ - [`onSubmit: (values: Values, formikBag: FormikBag) => void`](#onsubmit-values-values-formikbag-formikbag--void)
+ - [`validate?: (values: Values) => FormikError | Promise`](#validate-values-values--formikerrorvalues--promiseany)
+ - [`validateOnBlur?: boolean`](#validateonblur-boolean)
+ - [`validateOnChange?: boolean`](#validateonchange-boolean)
+ - [`validationSchema?: Schema | (() => Schema)`](#validationschema-schema----schema)
+ - [``](#field-)
+ - [`validate?: (value: any) => undefined | string | Promise`](#validate-value-any--undefined--string--promiseany)
+ - [``](#fieldarray)
+ - [`name: string`](#name-string)
+ - [`validateOnChange?: boolean`](#validateonchange-boolean-1)
+ - [FieldArray Validation Gotchas](#fieldarray-validation-gotchas)
+ - [FieldArray Helpers](#fieldarray-helpers)
+ - [FieldArray render methods](#fieldarray-render-methods)
+ - [`render: (arrayHelpers: ArrayHelpers) => React.ReactNode`](#render-arrayhelpers-arrayhelpers--reactreactnode)
+ - [`component: React.ReactNode`](#component-reactreactnode)
+ - [``](#form-)
+ - [`withFormik(options)`](#withformikoptions)
+ - [`options`](#options)
+ - [`displayName?: string`](#displayname-string)
+ - [`enableReinitialize?: boolean`](#enablereinitialize-boolean-1)
+ - [`handleSubmit: (values: Values, formikBag: FormikBag) => void`](#handlesubmit-values-values-formikbag-formikbag--void)
+ - [The "FormikBag":](#the-formikbag)
+ - [`isInitialValid?: boolean | (props: Props) => boolean`](#isinitialvalid-boolean--props-props--boolean)
+ - [`mapPropsToValues?: (props: Props) => Values`](#mappropstovalues-props-props--values)
+ - [`validate?: (values: Values, props: Props) => FormikError | Promise`](#validate-values-values-props-props--formikerrorvalues--promiseany)
+ - [`validateOnBlur?: boolean`](#validateonblur-boolean-1)
+ - [`validateOnChange?: boolean`](#validateonchange-boolean-2)
+ - [`validationSchema?: Schema | ((props: Props) => Schema)`](#validationschema-schema--props-props--schema)
+ - [Injected props and methods](#injected-props-and-methods)
+- [Organizations and projects using Formik](#organizations-and-projects-using-formik)
+- [Authors](#authors)
+- [Contributors](#contributors)
@@ -1368,6 +1368,76 @@ export const FriendList = () => (
The name or path to the relevant key in [`values`].
+##### `validateOnChange?: boolean`
+
+Default is `true`. Determines if form validation should or should not be run _after_ any array manipulations.
+
+#### FieldArray Validation Gotchas
+
+Validation can be tricky with ``.
+
+If you use [`validationSchema`] and your form has array validation requirements (like a min length) as well as nested array field requirements, displaying errors can be tricky. Formik/Yup will show validation errors inside out. For example,
+
+```js
+const schema = Yup.object().shape({
+ friends: Yup.array()
+ .of(
+ Yup.object().shape({
+ name: Yup.string()
+ .min(4, 'too short')
+ .required('Required'), // these constraints take precedence
+ salary: Yup.string()
+ .min(3, 'cmon')
+ .required('Required'), // these constraints take precedence
+ })
+ )
+ .required('Must have friends') // these constraints are shown if and only if inner constraints are satisfied
+ .min(3, 'Minimum of 3 friends'),
+});
+```
+
+Since Yup and your custom validation function should always output error messages as strings, you'll need to sniff whether your nested error is an array or a string when you go to display it.
+
+So...to display `'Must have friends'` and `'Minimum of 3 friends'` (our example's array validation contstraints)...
+
+**_Bad_**
+
+```js
+// within a `FieldArray`'s render
+const FriendArrayErrors = errors =>
+ errors.friends ? {errors.friends}
: null; // app will crash
+```
+
+**_Good_**
+
+```js
+// within a `FieldArray`'s render
+const FriendArrayErrors = errors =>
+ typeof friends === 'string' ? {errors.friends}
: null;
+```
+
+For the nested field errors, you should assume that no part of the object is defined unless you've checked for it. Thus, you may want to do yourself a favor and make a custom `` component that looks like this:
+
+```js
+import { Field, getIn } from 'formik';
+
+const ErrorMessage = ({ name }) => (
+ {
+ const error = getIn(form.errors, name);
+ const touch = getIn(form.touched, name);
+ return touch && error ? error : null;
+ }}
+ />
+);
+
+// Usage
+; // => null, 'too short', or 'required'
+```
+
+_NOTE_: In Formik v0.12 / 1.0, a new `meta` prop may be be added to `Field` and `FieldArray` that will give you relevant metadata such as `error` & `touch`, which will save you from having to use Formik or lodash's getIn or checking if the path is defined on your own.
+
#### FieldArray Helpers
The following methods are made available via render props.
diff --git a/package.json b/package.json
index 84b4a5c64..5281440c6 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"@types/lodash.topath": "4.5.3",
"@types/prop-types": "15.5.1",
"@types/react": "16.0.28",
+ "@types/react-dom": "^16.0.3",
"@types/react-native": "^0.52.8",
"@types/react-test-renderer": "15.5.2",
"@types/warning": "^3.0.0",
diff --git a/src/Field.tsx b/src/Field.tsx
index 1c73b85d9..7be4c5ae4 100644
--- a/src/Field.tsx
+++ b/src/Field.tsx
@@ -1,6 +1,6 @@
import * as PropTypes from 'prop-types';
import * as React from 'react';
-import { dlv, isPromise } from './utils';
+import { getIn, isPromise } from './utils';
import { FormikProps } from './formik';
import { isFunction, isEmptyChildren } from './utils';
@@ -48,7 +48,10 @@ export interface FieldConfig {
/**
* Field component to render. Can either be a string like 'select' or a component.
*/
- component?: string | React.ComponentType> | React.ComponentType;
+ component?:
+ | string
+ | React.ComponentType>
+ | React.ComponentType;
/**
* Render prop (works like React router's } />)
@@ -167,7 +170,7 @@ export class Field extends React.Component<
value:
props.type === 'radio' || props.type === 'checkbox'
? props.value // React uses checked={} for these inputs
- : dlv(formik.values, name),
+ : getIn(formik.values, name),
name,
onChange: validate ? this.handleChange : formik.handleChange,
onBlur: validate ? this.handleBlur : formik.handleBlur,
diff --git a/src/FieldArray.tsx b/src/FieldArray.tsx
index 8ed726b3e..05b3883de 100644
--- a/src/FieldArray.tsx
+++ b/src/FieldArray.tsx
@@ -1,12 +1,14 @@
+import * as PropTypes from 'prop-types';
import * as React from 'react';
-import { dlv, setDeep, isEmptyChildren } from './utils';
+import { FormikProps, FormikState, isFunction } from './formik';
+import { isEmptyChildren, getIn, setIn } from './utils';
import { SharedRenderProps } from './types';
-import * as PropTypes from 'prop-types';
-import { FormikProps, FormikState } from './formik';
export type FieldArrayConfig = {
/** Really the path to the array field to be updated */
name: string;
+ /** Should field array validate the form AFTER array updates/changes? */
+ validateOnChange?: boolean;
} & SharedRenderProps }>;
export interface ArrayHelpers {
@@ -52,6 +54,9 @@ export const insert = (array: any[], index: number, value: any) => {
};
export class FieldArray extends React.Component {
+ static defaultProps = {
+ validateOnChange: true,
+ };
static contextTypes = {
formik: PropTypes.object,
};
@@ -68,18 +73,31 @@ export class FieldArray extends React.Component {
alterTouched: boolean,
alterErrors: boolean
) => {
- const { setFormikState, values, touched, errors } = this.context.formik;
- const { name } = this.props;
- setFormikState((prevState: FormikState) => ({
- ...prevState,
- values: setDeep(name, fn(dlv(values, name)), prevState.values),
- errors: alterErrors
- ? setDeep(name, fn(dlv(errors, name)), prevState.errors)
- : prevState.errors,
- touched: alterTouched
- ? setDeep(name, fn(dlv(touched, name)), prevState.touched)
- : prevState.touched,
- }));
+ const {
+ setFormikState,
+ validateForm,
+ values,
+ touched,
+ errors,
+ } = this.context.formik;
+ const { name, validateOnChange } = this.props;
+ setFormikState(
+ (prevState: FormikState) => ({
+ ...prevState,
+ values: setIn(prevState.values, name, fn(getIn(values, name))),
+ errors: alterErrors
+ ? setIn(prevState.errors, name, fn(getIn(errors, name)))
+ : prevState.errors,
+ touched: alterTouched
+ ? setIn(prevState.touched, name, fn(getIn(touched, name)))
+ : prevState.touched,
+ }),
+ () => {
+ if (validateOnChange) {
+ validateForm();
+ }
+ }
+ );
};
push = (value: any) =>
@@ -128,12 +146,14 @@ export class FieldArray extends React.Component {
let result: any;
this.updateArrayField(
// so this gets call 3 times
- (array: any[]) => {
- const copy = [...(array || [])];
+ (array?: any[]) => {
+ const copy = array ? [...array] : [];
if (!result) {
result = copy[index];
}
- copy.splice(index, 1);
+ if (isFunction(copy.splice)) {
+ copy.splice(index, 1);
+ }
return copy;
},
true,
diff --git a/src/formik.tsx b/src/formik.tsx
index 521fe60d1..b72e6e475 100644
--- a/src/formik.tsx
+++ b/src/formik.tsx
@@ -8,7 +8,7 @@ import {
isPromise,
isReactNative,
isEmptyChildren,
- setDeep,
+ setIn,
setNestedObjectValues,
} from './utils';
@@ -437,11 +437,11 @@ export class Formik extends React.Component<
// Set form fields by name
this.setState(prevState => ({
...prevState,
- values: setDeep(field, val, prevState.values),
+ values: setIn(prevState.values, field, val),
}));
if (this.props.validateOnChange) {
- this.runValidations(setDeep(field, val, this.state.values));
+ this.runValidations(setIn(this.state.values, field, val));
}
};
@@ -454,7 +454,7 @@ export class Formik extends React.Component<
this.setState(
prevState => ({
...prevState,
- values: setDeep(field, value, prevState.values),
+ values: setIn(prevState.values, field, value),
}),
() => {
if (this.props.validateOnChange && shouldValidate) {
@@ -538,7 +538,7 @@ export class Formik extends React.Component<
}
this.setState(prevState => ({
- touched: setDeep(field, true, prevState.touched),
+ touched: setIn(prevState.touched, field, true),
}));
if (this.props.validateOnBlur) {
@@ -555,7 +555,7 @@ export class Formik extends React.Component<
this.setState(
prevState => ({
...prevState,
- touched: setDeep(field, touched, prevState.touched),
+ touched: setIn(prevState.touched, field, touched),
}),
() => {
if (this.props.validateOnBlur && shouldValidate) {
@@ -569,7 +569,7 @@ export class Formik extends React.Component<
// Set form field by name
this.setState(prevState => ({
...prevState,
- errors: setDeep(field, message, prevState.errors),
+ errors: setIn(prevState.errors, field, message),
}));
};
@@ -695,7 +695,7 @@ export function yupToFormErrors(yupError: any): FormikErrors {
let errors: any = {} as FormikErrors;
for (let err of yupError.inner) {
if (!errors[err.path]) {
- errors = setDeep(err.path, err.message, errors);
+ errors = setIn(errors, err.path, err.message);
}
}
return errors;
@@ -724,3 +724,4 @@ export * from './Field';
export * from './Form';
export * from './withFormik';
export * from './FieldArray';
+export * from './utils';
diff --git a/src/utils.ts b/src/utils.ts
index a4ad6b909..72652df27 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -3,9 +3,9 @@ import toPath from 'lodash.topath';
import cloneDeep from 'lodash.clonedeep';
/**
- * @private Deeply get a value from an object via it's path.
+ * Deeply get a value from an object via it's path.
*/
-export function dlv(
+export function getIn(
obj: any,
key: string | string[],
def?: any,
@@ -19,10 +19,10 @@ export function dlv(
}
/**
- * @private Deeply set a value from in object via it's path.
- * See https://github.com/developit/linkstate
+ * Deeply set a value from in object via it's path.
+ * @see https://github.com/developit/linkstate
*/
-export function setDeep(path: string, value: any, obj: any): any {
+export function setIn(obj: any, path: string, value: any): any {
let res: any = {};
let resVal: any = res;
let i = 0;
diff --git a/test/utils.test.tsx b/test/utils.test.tsx
index a493ed8e4..bb4c98b0b 100644
--- a/test/utils.test.tsx
+++ b/test/utils.test.tsx
@@ -1,4 +1,4 @@
-import { setDeep, setNestedObjectValues } from '../src/utils';
+import { setIn, setNestedObjectValues } from '../src/utils';
describe('utils', () => {
describe('setNestedObjectValues', () => {
@@ -126,31 +126,31 @@ describe('utils', () => {
});
});
- describe('setDeep', () => {
+ describe('setIn', () => {
it('sets flat value', () => {
const obj = { x: 'y' };
- const newObj = setDeep('flat', 'value', obj);
+ const newObj = setIn(obj, 'flat', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', flat: 'value' });
});
it('sets nested value', () => {
const obj = { x: 'y' };
- const newObj = setDeep('nested.value', 'nested value', obj);
+ const newObj = setIn(obj, 'nested.value', 'nested value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: { value: 'nested value' } });
});
it('updates nested value', () => {
const obj = { x: 'y', nested: { value: 'a' } };
- const newObj = setDeep('nested.value', 'b', obj);
+ const newObj = setIn(obj, 'nested.value', 'b');
expect(obj).toEqual({ x: 'y', nested: { value: 'a' } });
expect(newObj).toEqual({ x: 'y', nested: { value: 'b' } });
});
it('updates deep nested value', () => {
const obj = { x: 'y', twofoldly: { nested: { value: 'a' } } };
- const newObj = setDeep('twofoldly.nested.value', 'b', obj);
+ const newObj = setIn(obj, 'twofoldly.nested.value', 'b');
expect(obj.twofoldly.nested === newObj.twofoldly.nested).toEqual(false); // fails, same object still
expect(obj).toEqual({ x: 'y', twofoldly: { nested: { value: 'a' } } }); // fails, it's b here, too
expect(newObj).toEqual({ x: 'y', twofoldly: { nested: { value: 'b' } } }); // works ofc
@@ -158,101 +158,35 @@ describe('utils', () => {
it('sets new array', () => {
const obj = { x: 'y' };
- const newObj = setDeep('nested.0', 'value', obj);
+ const newObj = setIn(obj, 'nested.0', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: ['value'] });
});
it('updates nested array value', () => {
const obj = { x: 'y', nested: ['a'] };
- const newObj = setDeep('nested[0]', 'b', obj);
+ const newObj = setIn(obj, 'nested[0]', 'b');
expect(obj).toEqual({ x: 'y', nested: ['a'] });
expect(newObj).toEqual({ x: 'y', nested: ['b'] });
});
it('adds new item to nested array', () => {
const obj = { x: 'y', nested: ['a'] };
- const newObj = setDeep('nested.1', 'b', obj);
+ const newObj = setIn(obj, 'nested.1', 'b');
expect(obj).toEqual({ x: 'y', nested: ['a'] });
expect(newObj).toEqual({ x: 'y', nested: ['a', 'b'] });
});
it('sticks to object with int key when defined', () => {
const obj = { x: 'y', nested: { 0: 'a' } };
- const newObj = setDeep('nested.0', 'b', obj);
+ const newObj = setIn(obj, 'nested.0', 'b');
expect(obj).toEqual({ x: 'y', nested: { 0: 'a' } });
expect(newObj).toEqual({ x: 'y', nested: { 0: 'b' } });
});
it('supports bracket path', () => {
const obj = { x: 'y' };
- const newObj = setDeep('nested[0]', 'value', obj);
- expect(obj).toEqual({ x: 'y' });
- expect(newObj).toEqual({ x: 'y', nested: ['value'] });
- });
- });
-
- describe('setDeep', () => {
- it('sets flat value', () => {
- const obj = { x: 'y' };
- const newObj = setDeep('flat', 'value', obj);
- expect(obj).toEqual({ x: 'y' });
- expect(newObj).toEqual({ x: 'y', flat: 'value' });
- });
-
- it('sets nested value', () => {
- const obj = { x: 'y' };
- const newObj = setDeep('nested.value', 'nested value', obj);
- expect(obj).toEqual({ x: 'y' });
- expect(newObj).toEqual({ x: 'y', nested: { value: 'nested value' } });
- });
-
- it('updates nested value', () => {
- const obj = { x: 'y', nested: { value: 'a' } };
- const newObj = setDeep('nested.value', 'b', obj);
- expect(obj).toEqual({ x: 'y', nested: { value: 'a' } });
- expect(newObj).toEqual({ x: 'y', nested: { value: 'b' } });
- });
-
- it('updates deep nested value', () => {
- const obj = { x: 'y', twofoldly: { nested: { value: 'a' } } };
- const newObj = setDeep('twofoldly.nested.value', 'b', obj);
- expect(obj.twofoldly.nested === newObj.twofoldly.nested).toEqual(false); // fails, same object still
- expect(obj).toEqual({ x: 'y', twofoldly: { nested: { value: 'a' } } }); // fails, it's b here, too
- expect(newObj).toEqual({ x: 'y', twofoldly: { nested: { value: 'b' } } }); // works ofc
- });
-
- it('sets new array', () => {
- const obj = { x: 'y' };
- const newObj = setDeep('nested.0', 'value', obj);
- expect(obj).toEqual({ x: 'y' });
- expect(newObj).toEqual({ x: 'y', nested: ['value'] });
- });
-
- it('updates nested array value', () => {
- const obj = { x: 'y', nested: ['a'] };
- const newObj = setDeep('nested[0]', 'b', obj);
- expect(obj).toEqual({ x: 'y', nested: ['a'] });
- expect(newObj).toEqual({ x: 'y', nested: ['b'] });
- });
-
- it('adds new item to nested array', () => {
- const obj = { x: 'y', nested: ['a'] };
- const newObj = setDeep('nested.1', 'b', obj);
- expect(obj).toEqual({ x: 'y', nested: ['a'] });
- expect(newObj).toEqual({ x: 'y', nested: ['a', 'b'] });
- });
-
- it('sticks to object with int key when defined', () => {
- const obj = { x: 'y', nested: { 0: 'a' } };
- const newObj = setDeep('nested.0', 'b', obj);
- expect(obj).toEqual({ x: 'y', nested: { 0: 'a' } });
- expect(newObj).toEqual({ x: 'y', nested: { 0: 'b' } });
- });
-
- it('supports bracket path', () => {
- const obj = { x: 'y' };
- const newObj = setDeep('nested[0]', 'value', obj);
+ const newObj = setIn(obj, 'nested[0]', 'value');
expect(obj).toEqual({ x: 'y' });
expect(newObj).toEqual({ x: 'y', nested: ['value'] });
});
diff --git a/yarn.lock b/yarn.lock
index 20d894330..216f8428a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -53,6 +53,13 @@
version "15.5.1"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.1.tgz#1ecf52621299e65b855374337fb11fd2d1066fc1"
+"@types/react-dom@^16.0.3":
+ version "16.0.3"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64"
+ dependencies:
+ "@types/node" "*"
+ "@types/react" "*"
+
"@types/react-native@^0.52.8":
version "0.52.8"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.52.8.tgz#dd5aa7c4eb944d7c0b2b249a2bb811aa119f49c6"