Skip to content

Commit

Permalink
Fix #3948 - Changing state was also causing change of initial value (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
DeveloperRaj authored Apr 10, 2024
1 parent c6ceb65 commit f57ca9b
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-vans-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'formik': patch
---

Changing the state inside formik was changing reference of initialValues provided via props, deep cloning the initialvalues will fix it.
9 changes: 5 additions & 4 deletions packages/formik/src/Formik.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import deepmerge from 'deepmerge';
import isPlainObject from 'lodash/isPlainObject';
import cloneDeep from 'lodash/cloneDeep';
import * as React from 'react';
import isEqual from 'react-fast-compare';
import invariant from 'tiny-warning';
Expand Down Expand Up @@ -173,10 +174,10 @@ export function useFormik<Values extends FormikValues = FormikValues>({

const [, setIteration] = React.useState(0);
const stateRef = React.useRef<FormikState<Values>>({
values: props.initialValues,
errors: props.initialErrors || emptyErrors,
touched: props.initialTouched || emptyTouched,
status: props.initialStatus,
values: cloneDeep(props.initialValues),

This comment has been minimized.

Copy link
@densk1

densk1 May 8, 2024

Think this breaks reference equality for options and values. I'll add a test case to issues if I can create one.

errors: cloneDeep(props.initialErrors) || emptyErrors,
touched: cloneDeep(props.initialTouched) || emptyTouched,
status: cloneDeep(props.initialStatus),
isSubmitting: false,
isValidating: false,
submitCount: 0,
Expand Down
44 changes: 44 additions & 0 deletions packages/formik/test/Formik.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ const InitialValues = {
age: 30,
};

const InitialValuesWithNestedObject = {
content: {
items: [
{
cards: [
{
desc: 'Initial Desc',
},
],
},
],
},
};

function renderFormik<V extends FormikValues = Values>(
props?: Partial<FormikConfig<V>>
) {
Expand Down Expand Up @@ -1454,4 +1468,34 @@ describe('<Formik>', () => {

expect(innerRef.current).toEqual(getProps());
});

it('should not modify original initialValues object', () => {
render(
<Formik initialValues={InitialValuesWithNestedObject} onSubmit={noop}>
{formikProps => (
<input
data-testid="desc-input"
value={formikProps.values.content.items[0].cards[0].desc}
onChange={e => {
const copy = { ...formikProps.values.content };
copy.items[0].cards[0].desc = e.target.value;
formikProps.setValues({
...formikProps.values,
content: copy,
});
}}
/>
)}
</Formik>
);
const input = screen.getByTestId('desc-input');

fireEvent.change(input, {
target: {
value: 'New Value',
},
});

expect(InitialValuesWithNestedObject.content.items[0].cards[0].desc).toEqual('Initial Desc');
});
});

0 comments on commit f57ca9b

Please sign in to comment.