Skip to content

Commit f57ca9b

Browse files
authored
Fix #3948 - Changing state was also causing change of initial value (#3949)
Resolves Issue #3948 This addresses the problem of the dirty field not updating when the value of a nested object changes. The root cause of this issue is that the `initialValues` coming from props were directly assigned to the `useRef`, which did not perform a deep copy. Without a deep copy, it was modifying the original `initialValues` props along with the current state. Consequently, when comparing them for equality, the result was true
1 parent c6ceb65 commit f57ca9b

File tree

3 files changed

+54
-4
lines changed

3 files changed

+54
-4
lines changed

.changeset/empty-vans-bathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'formik': patch
3+
---
4+
5+
Changing the state inside formik was changing reference of initialValues provided via props, deep cloning the initialvalues will fix it.

packages/formik/src/Formik.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import deepmerge from 'deepmerge';
22
import isPlainObject from 'lodash/isPlainObject';
3+
import cloneDeep from 'lodash/cloneDeep';
34
import * as React from 'react';
45
import isEqual from 'react-fast-compare';
56
import invariant from 'tiny-warning';
@@ -173,10 +174,10 @@ export function useFormik<Values extends FormikValues = FormikValues>({
173174

174175
const [, setIteration] = React.useState(0);
175176
const stateRef = React.useRef<FormikState<Values>>({
176-
values: props.initialValues,
177-
errors: props.initialErrors || emptyErrors,
178-
touched: props.initialTouched || emptyTouched,
179-
status: props.initialStatus,
177+
values: cloneDeep(props.initialValues),
178+
errors: cloneDeep(props.initialErrors) || emptyErrors,
179+
touched: cloneDeep(props.initialTouched) || emptyTouched,
180+
status: cloneDeep(props.initialStatus),
180181
isSubmitting: false,
181182
isValidating: false,
182183
submitCount: 0,

packages/formik/test/Formik.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ const InitialValues = {
6161
age: 30,
6262
};
6363

64+
const InitialValuesWithNestedObject = {
65+
content: {
66+
items: [
67+
{
68+
cards: [
69+
{
70+
desc: 'Initial Desc',
71+
},
72+
],
73+
},
74+
],
75+
},
76+
};
77+
6478
function renderFormik<V extends FormikValues = Values>(
6579
props?: Partial<FormikConfig<V>>
6680
) {
@@ -1454,4 +1468,34 @@ describe('<Formik>', () => {
14541468

14551469
expect(innerRef.current).toEqual(getProps());
14561470
});
1471+
1472+
it('should not modify original initialValues object', () => {
1473+
render(
1474+
<Formik initialValues={InitialValuesWithNestedObject} onSubmit={noop}>
1475+
{formikProps => (
1476+
<input
1477+
data-testid="desc-input"
1478+
value={formikProps.values.content.items[0].cards[0].desc}
1479+
onChange={e => {
1480+
const copy = { ...formikProps.values.content };
1481+
copy.items[0].cards[0].desc = e.target.value;
1482+
formikProps.setValues({
1483+
...formikProps.values,
1484+
content: copy,
1485+
});
1486+
}}
1487+
/>
1488+
)}
1489+
</Formik>
1490+
);
1491+
const input = screen.getByTestId('desc-input');
1492+
1493+
fireEvent.change(input, {
1494+
target: {
1495+
value: 'New Value',
1496+
},
1497+
});
1498+
1499+
expect(InitialValuesWithNestedObject.content.items[0].cards[0].desc).toEqual('Initial Desc');
1500+
});
14571501
});

0 commit comments

Comments
 (0)