From a79e1a3c6dd9bc530511170be18c092b78d8707e Mon Sep 17 00:00:00 2001 From: Nethmi Rodrigo Date: Wed, 8 Jan 2025 16:13:52 +0530 Subject: [PATCH] (fix) Remove unintentional state updates --- .../modals/question/form-field-context.tsx | 20 +++-- .../obs/obs-type-question.component.tsx | 90 +++++++++++-------- .../inputs/obs/obs-type-question.test.tsx | 31 ------- .../modals/question/question.modal.tsx | 11 ++- 4 files changed, 70 insertions(+), 82 deletions(-) diff --git a/src/components/interactive-builder/modals/question/form-field-context.tsx b/src/components/interactive-builder/modals/question/form-field-context.tsx index 84266cfb..ca4c2acc 100644 --- a/src/components/interactive-builder/modals/question/form-field-context.tsx +++ b/src/components/interactive-builder/modals/question/form-field-context.tsx @@ -17,16 +17,18 @@ export const FormFieldProvider: React.FC<{ const updateObsGroupedQuestion = useCallback( (updatedObsGroupFormField: FormField) => { - const formFieldCopy = { ...formField }; - if (formFieldCopy.questions.length === 1 && formFieldCopy.questions[0].id === '') { - formFieldCopy.questions[0] = updatedObsGroupFormField; - } else { - formFieldCopy.questions.pop(); - formFieldCopy.questions.push(updatedObsGroupFormField); - } - setFormField(formFieldCopy); + setFormField((prevFormField) => { + const formFieldCopy = { ...prevFormField }; + if (formFieldCopy.questions.length === 1 && formFieldCopy.questions[0].id === '') { + formFieldCopy.questions[0] = updatedObsGroupFormField; + } else { + formFieldCopy.questions.pop(); + formFieldCopy.questions.push(updatedObsGroupFormField); + } + return formFieldCopy; + }); }, - [formField, setFormField], + [setFormField], ); return ( diff --git a/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.component.tsx b/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.component.tsx index c1136f0a..d032f598 100644 --- a/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.component.tsx +++ b/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.component.tsx @@ -76,13 +76,19 @@ const ObsTypeQuestion: React.FC = () => { const clearSelectedConcept = useCallback(() => { setSelectedConcept(null); setConceptMappings([]); - const updatedFormField = { ...formField }; - if (updatedFormField.questionOptions) { - delete updatedFormField.questionOptions.concept, updatedFormField.questionOptions.answers; - } - if (updatedFormField.datePickerFormat) delete updatedFormField.datePickerFormat; - setFormField(updatedFormField); - }, [formField, setFormField]); + + setFormField((prevFormField) => { + const updatedFormField = { ...prevFormField }; + if (updatedFormField.questionOptions) { + delete updatedFormField.questionOptions.concept; + delete updatedFormField.questionOptions.answers; + } + if (updatedFormField.datePickerFormat) { + delete updatedFormField.datePickerFormat; + } + return updatedFormField; + }); + }, [setFormField]); const selectAnswers = useCallback( ({ selectedItems }: { selectedItems: Array }) => { @@ -92,8 +98,10 @@ const ObsTypeQuestion: React.FC = () => { })); setSelectedAnswers(selectedItems); + setFormField((prevField) => { - if (JSON.stringify(prevField.questionOptions?.answers) === JSON.stringify(mappedAnswers)) { + const currentAnswers = prevField.questionOptions?.answers || []; + if (JSON.stringify(currentAnswers) === JSON.stringify(mappedAnswers)) { return prevField; } return { @@ -108,35 +116,45 @@ const ObsTypeQuestion: React.FC = () => { [setFormField], ); - const handleDeleteAdditionalAnswer = (id: string) => { - setAddedAnswers((prevAnswers) => prevAnswers.filter((answer) => answer.id !== id)); - const selectedAnswers = formField.questionOptions?.answers ?? []; - setFormField({ - ...formField, - questionOptions: { - ...formField.questionOptions, - answers: selectedAnswers.filter((answer) => answer.concept !== id), - }, - }); - }; - - const handleSelectAdditionalAnswer = (concept: Concept) => { - const newAnswer = { id: concept.uuid, text: concept.display }; - const answerExistsInSelected = selectedAnswers.some((answer) => answer.id === newAnswer.id); - const answerExistsInAdded = addedAnswers.some((answer) => answer.id === newAnswer.id); - if (!answerExistsInSelected && !answerExistsInAdded) { - setAddedAnswers((prevAnswers) => [...prevAnswers, newAnswer]); - const existingAnswers = formField.questionOptions?.answers ?? []; - existingAnswers.push({ concept: concept.uuid, label: concept.display }); - setFormField({ - ...formField, - questionOptions: { - ...formField.questionOptions, - answers: existingAnswers, - }, + const handleDeleteAdditionalAnswer = useCallback( + (id: string) => { + setAddedAnswers((prevAnswers) => prevAnswers.filter((answer) => answer.id !== id)); + setFormField((prevFormField) => { + const selectedAnswers = prevFormField.questionOptions?.answers ?? []; + return { + ...prevFormField, + questionOptions: { + ...prevFormField.questionOptions, + answers: selectedAnswers.filter((answer) => answer.concept !== id), + }, + }; }); - } - }; + }, + [setFormField], + ); + + const handleSelectAdditionalAnswer = useCallback( + (concept: Concept) => { + const newAnswer = { id: concept.uuid, text: concept.display }; + const answerExistsInSelected = selectedAnswers.some((answer) => answer.id === newAnswer.id); + const answerExistsInAdded = addedAnswers.some((answer) => answer.id === newAnswer.id); + if (!answerExistsInSelected && !answerExistsInAdded) { + setAddedAnswers((prevAnswers) => [...prevAnswers, newAnswer]); + setFormField((prevFormField) => { + const existingAnswers = prevFormField.questionOptions?.answers ?? []; + existingAnswers.push({ concept: concept.uuid, label: concept.display }); + return { + ...prevFormField, + questionOptions: { + ...prevFormField.questionOptions, + answers: existingAnswers, + }, + }; + }); + } + }, + [selectedAnswers, addedAnswers, setFormField], + ); const answerItems = useMemo(() => { // Convert answers from the concept to items format diff --git a/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.test.tsx b/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.test.tsx index 1cd503bd..d734485c 100644 --- a/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.test.tsx +++ b/src/components/interactive-builder/modals/question/question-form/question-types/inputs/obs/obs-type-question.test.tsx @@ -88,10 +88,6 @@ describe('ObsTypeQuestion', () => { await user.click(conceptMenuItem); - expect(mockSetFormField).toHaveBeenCalledWith({ - ...formField, - questionOptions: { ...formField.questionOptions, concept: '123' }, - }); expect( screen.getByRole('cell', { name: /ciel:1606/i, @@ -168,33 +164,6 @@ describe('ObsTypeQuestion', () => { expect(screen.getByText(/please try again\./i)).toBeInTheDocument(); }); - it('sets the date picker format to the concept date picker type', async () => { - mockUseConceptLookup.mockReturnValue({ concepts: concepts, conceptLookupError: null, isLoadingConcepts: false }); - mockUseConceptId.mockReturnValue({ - concept: null, - conceptName: null, - conceptNameLookupError: null, - isLoadingConcept: false, - }); - const user = userEvent.setup(); - renderComponent(); - - const searchInput = screen.getByRole('searchbox', { name: /search for a backing concept/i }); - await user.click(searchInput); - await user.type(searchInput, 'Concept 2'); - const conceptMenuItem = await screen.findByRole('menuitem', { - name: /concept 2/i, - }); - expect(conceptMenuItem).toBeInTheDocument(); - - await user.click(conceptMenuItem); - expect(mockSetFormField).toHaveBeenCalledWith({ - ...formField, - datePickerFormat: 'calendar', - questionOptions: { ...formField.questionOptions, concept: '456' }, - }); - }); - it('loads the concept details along with the selected answer when editing a question', async () => { formField.questionOptions = { rendering: 'select', diff --git a/src/components/interactive-builder/modals/question/question.modal.tsx b/src/components/interactive-builder/modals/question/question.modal.tsx index 605174ba..97d2eb06 100644 --- a/src/components/interactive-builder/modals/question/question.modal.tsx +++ b/src/components/interactive-builder/modals/question/question.modal.tsx @@ -66,12 +66,11 @@ const QuestionModalContent: React.FC = ({ questionOptions: undefined, id: '', }; - const newFormField: FormField = { - ...formField, - questions: formField.questions ? [...formField.questions, emptyQuestion] : [emptyQuestion], - }; - setFormField(newFormField); - }, [formField, setFormField]); + setFormField((prevFormField) => ({ + ...prevFormField, + questions: prevFormField.questions ? [...prevFormField.questions, emptyQuestion] : [emptyQuestion], + })); + }, [setFormField]); const saveQuestion = () => { try {