From f10c73f1f51a9dcb7eaa5204ce52ab45cf1c84bb Mon Sep 17 00:00:00 2001 From: Josh Salisbury Date: Thu, 11 Feb 2021 10:27:51 -0600 Subject: [PATCH] Use `useFormContext` from `react-hook-form` Instead of passing form props down through components we now use `useFormContext` that allows the context to be passed to nested components without having to do so with props. This should help with readability as we need to use the form context in deeply nested components. --- .../components/Navigator/__tests__/index.js | 34 +++-- frontend/src/components/Navigator/index.js | 16 ++- .../ActivityReport/Pages/ReviewSubmit.js | 5 +- .../Pages/__tests__/ReviewSubmit.js | 24 ++-- .../Pages/__tests__/goalsObjectives.js | 9 +- .../ActivityReport/Pages/activitySummary.js | 29 ++-- .../ActivityReport/Pages/goalsObjectives.js | 24 ++-- .../src/pages/ActivityReport/Pages/index.js | 2 - .../ActivityReport/Pages/topicsResources.js | 133 +++++++++--------- 9 files changed, 129 insertions(+), 147 deletions(-) diff --git a/frontend/src/components/Navigator/__tests__/index.js b/frontend/src/components/Navigator/__tests__/index.js index a11f34e5f7..8c6d6066b8 100644 --- a/frontend/src/components/Navigator/__tests__/index.js +++ b/frontend/src/components/Navigator/__tests__/index.js @@ -5,22 +5,31 @@ import { render, screen, waitFor, within, } from '@testing-library/react'; +import { useFormContext } from 'react-hook-form'; import Navigator from '../index'; import { NOT_STARTED } from '../constants'; +// eslint-disable-next-line react/prop-types +const Input = ({ name }) => { + const { register } = useFormContext(); + return ( + + ); +}; + const pages = [ { position: 1, path: 'first', label: 'first page', review: false, - render: (hookForm) => ( - + render: () => ( + ), }, { @@ -28,13 +37,8 @@ const pages = [ path: 'second', label: 'second page', review: false, - render: (hookForm) => ( - + render: () => ( + ), }, { @@ -42,7 +46,7 @@ const pages = [ label: 'review page', path: 'review', review: true, - render: (hookForm, allComplete, formData, onSubmit) => ( + render: (allComplete, formData, onSubmit) => (
diff --git a/frontend/src/components/Navigator/index.js b/frontend/src/components/Navigator/index.js index 004a177cfe..67ac22cd75 100644 --- a/frontend/src/components/Navigator/index.js +++ b/frontend/src/components/Navigator/index.js @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-props-no-spreading */ /* The navigator is a component used to show multiple form pages. It displays a stickied nav window on the left hand side with each page of the form listed. Clicking on an item in the nav list will @@ -6,7 +7,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; -import { useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form'; import { Form, Button, Grid } from '@trussworks/react-uswds'; import useDeepCompareEffect from 'use-deep-compare-effect'; import useInterval from '@use-it/interval'; @@ -125,10 +126,10 @@ function Navigator({ /> - + ); diff --git a/frontend/src/pages/ActivityReport/Pages/ReviewSubmit.js b/frontend/src/pages/ActivityReport/Pages/ReviewSubmit.js index 68a9974ff8..d0e7fd6d7e 100644 --- a/frontend/src/pages/ActivityReport/Pages/ReviewSubmit.js +++ b/frontend/src/pages/ActivityReport/Pages/ReviewSubmit.js @@ -4,6 +4,7 @@ import { Alert, Accordion, } from '@trussworks/react-uswds'; import { Helmet } from 'react-helmet'; +import { useFormContext } from 'react-hook-form'; import Container from '../../../components/Container'; import SubmitterReviewPage from './SubmitterReviewPage'; @@ -11,7 +12,6 @@ import ApproverReviewPage from './ApproverReviewPage'; import './ReviewSubmit.css'; const ReviewSubmit = ({ - hookForm, allComplete, onSubmit, onReview, @@ -20,7 +20,7 @@ const ReviewSubmit = ({ approvingManager, initialData, }) => { - const { handleSubmit, register, formState } = hookForm; + const { handleSubmit, register, formState } = useFormContext(); const { additionalNotes } = initialData; const { isValid } = formState; const valid = allComplete && isValid; @@ -108,7 +108,6 @@ ReviewSubmit.propTypes = { additionalNotes: PropTypes.string, }).isRequired, // eslint-disable-next-line react/forbid-prop-types - hookForm: PropTypes.object.isRequired, reviewItems: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string.isRequired, diff --git a/frontend/src/pages/ActivityReport/Pages/__tests__/ReviewSubmit.js b/frontend/src/pages/ActivityReport/Pages/__tests__/ReviewSubmit.js index 5e0a31baaf..2ca6eb2dd6 100644 --- a/frontend/src/pages/ActivityReport/Pages/__tests__/ReviewSubmit.js +++ b/frontend/src/pages/ActivityReport/Pages/__tests__/ReviewSubmit.js @@ -1,8 +1,9 @@ +/* eslint-disable react/jsx-props-no-spreading */ import '@testing-library/jest-dom'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; import userEvent from '@testing-library/user-event'; -import { useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form'; import ReviewSubmit from '../ReviewSubmit'; @@ -20,16 +21,17 @@ const RenderReview = ({ defaultValues: { ...initialData, approvingManagerId }, }); return ( - + + + ); }; diff --git a/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js b/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js index 6b615b6e53..d7cce51120 100644 --- a/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js +++ b/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js @@ -1,8 +1,9 @@ +/* eslint-disable react/jsx-props-no-spreading */ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import fetchMock from 'fetch-mock'; import React from 'react'; -import { useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form'; import join from 'url-join'; import goalsObjectives from '../goalsObjectives'; @@ -21,9 +22,9 @@ const RenderGoalsObjectives = ({ const activityRecipients = grantIds.map((id) => ({ activityRecipientId: id })); const data = { ...initialData, activityRecipientType, activityRecipients }; return ( - <> - {goalsObjectives.render(hookForm, {}, data)} - + + {goalsObjectives.render({}, data)} + ); }; diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 3dccf181e4..8189f24828 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -1,6 +1,7 @@ import React, { useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; +import { useFormContext } from 'react-hook-form'; import { Fieldset, Radio, Label, Grid, TextInput, Checkbox, @@ -17,14 +18,16 @@ import { } from '../constants'; const ActivitySummary = ({ - register, - watch, - setValue, - control, - getValues, recipients, collaborators, }) => { + const { + register, + watch, + setValue, + control, + getValues, + } = useFormContext(); const activityRecipientType = watch('activityRecipientType'); const startDate = watch('startDate'); const endDate = watch('endDate'); @@ -274,10 +277,6 @@ const ActivitySummary = ({ }; ActivitySummary.propTypes = { - register: PropTypes.func.isRequired, - watch: PropTypes.func.isRequired, - setValue: PropTypes.func.isRequired, - getValues: PropTypes.func.isRequired, collaborators: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string.isRequired, @@ -303,8 +302,6 @@ ActivitySummary.propTypes = { }), ), }).isRequired, - // eslint-disable-next-line react/forbid-prop-types - control: PropTypes.object.isRequired, }; const sections = [ @@ -361,19 +358,11 @@ export default { path: 'activity-summary', sections, review: false, - render: (hookForm, additionalData) => { - const { - register, watch, setValue, getValues, control, - } = hookForm; + render: (additionalData) => { const { recipients, collaborators } = additionalData; return ( ); diff --git a/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js b/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js index bd213fd797..a1fec8776a 100644 --- a/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js +++ b/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js @@ -5,13 +5,20 @@ import { Fieldset, Label, Textarea, } from '@trussworks/react-uswds'; import useDeepCompareEffect from 'use-deep-compare-effect'; +import { useFormContext } from 'react-hook-form'; import GoalPicker from './components/GoalPicker'; import { getGoals } from '../../../fetchers/activityReports'; const GoalsObjectives = ({ - control, grantIds, register, watch, setValue, activityRecipientType, + grantIds, activityRecipientType, }) => { + const { + control, + register, + watch, + setValue, + } = useFormContext(); const [availableGoals, updateAvailableGoals] = useState([]); const [loading, updateLoading] = useState(true); const goals = watch('goals'); @@ -62,12 +69,7 @@ const GoalsObjectives = ({ }; GoalsObjectives.propTypes = { - register: PropTypes.func.isRequired, - setValue: PropTypes.func.isRequired, grantIds: PropTypes.arrayOf(PropTypes.number).isRequired, - watch: PropTypes.func.isRequired, - // eslint-disable-next-line react/forbid-prop-types - control: PropTypes.object.isRequired, activityRecipientType: PropTypes.string.isRequired, }; @@ -94,10 +96,7 @@ export default { path: 'goals-objectives', review: false, sections, - render: (hookForm, additionalData, formData) => { - const { - register, watch, control, setValue, - } = hookForm; + render: (additionalData, formData) => { const recipients = formData.activityRecipients || []; const { activityRecipientType } = formData; const grantIds = recipients.map((r) => r.activityRecipientId); @@ -105,11 +104,6 @@ export default { ); }, diff --git a/frontend/src/pages/ActivityReport/Pages/index.js b/frontend/src/pages/ActivityReport/Pages/index.js index b8907e27c6..6f75e0d6ce 100644 --- a/frontend/src/pages/ActivityReport/Pages/index.js +++ b/frontend/src/pages/ActivityReport/Pages/index.js @@ -28,7 +28,6 @@ const reviewPage = { path: 'review', render: ( - hookForm, allComplete, formData, onSubmit, @@ -41,7 +40,6 @@ const reviewPage = { allComplete={allComplete} onSubmit={onSubmit} onReview={onReview} - hookForm={hookForm} approvingManager={approvingManager} reviewItems={ pages.map((p) => reviewItem(p.path, p.label, p.sections, formData)) diff --git a/frontend/src/pages/ActivityReport/Pages/topicsResources.js b/frontend/src/pages/ActivityReport/Pages/topicsResources.js index cb555a8edf..9b657d2d75 100644 --- a/frontend/src/pages/ActivityReport/Pages/topicsResources.js +++ b/frontend/src/pages/ActivityReport/Pages/topicsResources.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Controller } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import { Helmet } from 'react-helmet'; import { @@ -13,74 +13,72 @@ import FileUploader from '../../../components/FileUploader'; import { topics } from '../constants'; const TopicsResources = ({ - register, - control, reportId, -}) => ( - <> - - Topics and resources - -
-
-
- ({ value: topic, label: topic })) - } - /> -
-
-
-
-
- - -
-
- +}) => { + const { register, control } = useFormContext(); + return ( + <> + + Topics and resources + +
+
+
+ ({ value: topic, label: topic })) + } + /> +
+
+
+
+
+ + +
+
+ + ( + + )} + /> +
+
+
+
+ ( - + )} /> -
-
-
-
- - ( - - )} - /> -
- -); +
+ + ); +}; TopicsResources.propTypes = { - register: PropTypes.func.isRequired, - // eslint-disable-next-line react/forbid-prop-types - control: PropTypes.object.isRequired, reportId: PropTypes.node.isRequired, }; @@ -115,14 +113,9 @@ export default { path: 'topics-resources', sections, review: false, - render: (hookForm, additionalData, formData, reportId) => { - const { control, register } = hookForm; - return ( - - ); - }, + render: (additionalData, formData, reportId) => ( + + ), };