From f57ca9bc5ee3842d50f74f39b3fb36a744b55ae8 Mon Sep 17 00:00:00 2001 From: DeveloperRaj <40798951+DeveloperRaj@users.noreply.github.com> Date: Thu, 11 Apr 2024 00:16:56 +0530 Subject: [PATCH] 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 --- .changeset/empty-vans-bathe.md | 5 ++++ packages/formik/src/Formik.tsx | 9 +++--- packages/formik/test/Formik.test.tsx | 44 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 .changeset/empty-vans-bathe.md diff --git a/.changeset/empty-vans-bathe.md b/.changeset/empty-vans-bathe.md new file mode 100644 index 000000000..adfff99c5 --- /dev/null +++ b/.changeset/empty-vans-bathe.md @@ -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. diff --git a/packages/formik/src/Formik.tsx b/packages/formik/src/Formik.tsx index cd17adcd4..80d868665 100755 --- a/packages/formik/src/Formik.tsx +++ b/packages/formik/src/Formik.tsx @@ -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'; @@ -173,10 +174,10 @@ export function useFormik({ const [, setIteration] = React.useState(0); const stateRef = React.useRef>({ - values: props.initialValues, - errors: props.initialErrors || emptyErrors, - touched: props.initialTouched || emptyTouched, - status: props.initialStatus, + values: cloneDeep(props.initialValues), + errors: cloneDeep(props.initialErrors) || emptyErrors, + touched: cloneDeep(props.initialTouched) || emptyTouched, + status: cloneDeep(props.initialStatus), isSubmitting: false, isValidating: false, submitCount: 0, diff --git a/packages/formik/test/Formik.test.tsx b/packages/formik/test/Formik.test.tsx index 2acf332dd..98b6ef3c9 100644 --- a/packages/formik/test/Formik.test.tsx +++ b/packages/formik/test/Formik.test.tsx @@ -61,6 +61,20 @@ const InitialValues = { age: 30, }; +const InitialValuesWithNestedObject = { + content: { + items: [ + { + cards: [ + { + desc: 'Initial Desc', + }, + ], + }, + ], + }, +}; + function renderFormik( props?: Partial> ) { @@ -1454,4 +1468,34 @@ describe('', () => { expect(innerRef.current).toEqual(getProps()); }); + + it('should not modify original initialValues object', () => { + render( + + {formikProps => ( + { + const copy = { ...formikProps.values.content }; + copy.items[0].cards[0].desc = e.target.value; + formikProps.setValues({ + ...formikProps.values, + content: copy, + }); + }} + /> + )} + + ); + const input = screen.getByTestId('desc-input'); + + fireEvent.change(input, { + target: { + value: 'New Value', + }, + }); + + expect(InitialValuesWithNestedObject.content.items[0].cards[0].desc).toEqual('Initial Desc'); + }); });