setting error at child Component Code Preview I'm currently facing an issue in a React project where I have a form implemented in a parent component and validation errors being handled in a child component. Despite setting errors in the child component, the form state in the parent component does not seem to reflect these errors, resulting in the form not being marked as invalid.
Here's a brief overview of the setup:
The form is implemented in a parent component. Validation errors are being handled and set in a child component. Even though errors are present in the child component, the form state in the parent component does not show it as invalid. I've double-checked the communication between the parent and child components, and it seems like the errors are being correctly passed from the child to the parent. However, the form state doesn't seem to update accordingly.
I would appreciate any insights, suggestions, or examples of how to ensure that the form state in the parent component accurately reflects the validation errors set in the child component.
Thank you in advance for your help!
What I've Tried:
Verified that the errors are correctly set in the child component by console logging them. Confirmed that the error values are being passed from the child to the parent component through props. Checked that the parent component state is being updated with the correct error values. Examined the form validation logic in the parent component to ensure it's correctly utilizing the error values. Expected Outcome: I expected that when errors are present in the child component and passed to the parent component, the form state in the parent component should recognize these errors and mark the form as invalid. However, despite these efforts, the form state remains unaffected.
By providing this additional information, you give those helping you a clearer picture of your troubleshooting process and enable them to offer more targeted assistance.
import React, { useEffect } from 'react'
import { Form, Col } from 'react-bootstrap'
import { FormProvider, Controller, useFormContext } from 'react-hook-form'
import Attachments from '../../../../../components/Attachments'
import { DOE_SOURCE, BCM_REPORT_TYPE, COMMON_ROLES, TASK_NAME } from '../../../../../constants/appConstants'
import { IUiConfigurations } from '../../../../../constants/commonInterface'
import { IBcmKpi, getColorForCompliance } from '../KpiReport.dto'
import { ATTACHMENT_CONFIG } from '../../../../../constants/appAttachments'
import { restrictToTwoDecimals } from '../../../../../helpers/api/helper'
import { get } from 'lodash'
import { useSelector } from 'react-redux'
interface IbcmKpiProps {
taskName?:string | null,
bcmKpi: IBcmKpi,
kpiIndex: number,
registerLocation: any,
useFormMethods: any,
uiConfiguration: IUiConfigurations
jsonLocation: any,
calculateKpiResultScoreCompliance: any
calculateApplicableWeight: any
disableFields: boolean
setIsUploadedRequiredAttachments: any,
}
export const Kpi = ({ taskName,bcmKpi, kpiIndex, registerLocation, useFormMethods, uiConfiguration, jsonLocation, calculateKpiResultScoreCompliance, calculateApplicableWeight, disableFields, setIsUploadedRequiredAttachments }: IbcmKpiProps) => {
const watchNotApplicableValue = useFormMethods.watch(`${registerLocation}.notApplicable`);
const userInfo: any = useSelector((state: any) => state.userDetails);
const calculateApplicantScore = (action?: string) => {
// getting values outside performanceIndicatorValue object to get
const modifiedString = registerLocation.replace(/\.performanceIndicatorValue$/, '');
const deducatableKpiWeight = parseFloat(useFormMethods.getValues(`${modifiedString}.kpiWeight`))
calculateApplicableWeight(deducatableKpiWeight, action)
}
useEffect(() => {
if (watchNotApplicableValue) {
useFormMethods.setValue(`${registerLocation}.numerator`, '')
useFormMethods.setValue(`${registerLocation}.denominator`, '')
useFormMethods.setValue(`${registerLocation}.compliance`, '')
useFormMethods.trigger(`${registerLocation}.compliance`)
calculateApplicantScore('sub')
}
else if (watchNotApplicableValue == null) {
calculateApplicantScore()
}
else if (watchNotApplicableValue == false) {
calculateApplicantScore('add')
}
}, [watchNotApplicableValue]);
return (
<React.Fragment key={kpiIndex + (bcmKpi.kpiName || "")}>
{/* Row for BCM KPI */}
<tr >
<td colSpan={2} className="text-center align-middle"> {bcmKpi?.kpiId}</td>
<td className="text-center align-middle"> {bcmKpi?.kpiName}</td>
<td className="text-center align-middle"> {bcmKpi?.kpiWeight}</td>
<td className='align-middle text-center'>
<Form.Check
type="checkbox"
id="checkboxId"
{...useFormMethods.register(`${registerLocation}.notApplicable`)}
disabled={disableFields || (!useFormMethods.getValues(`${registerLocation}.comments`) && taskName !== TASK_NAME.SUBMIT_KPI)}
/>
</td>
<td className='align-middle text-center w-20'>
<FormProvider {...useFormMethods}>
<Col md={12}>
<NumeratorInput disableFields={disableFields || (!useFormMethods.getValues(`${registerLocation}.comments`) && taskName !== TASK_NAME.SUBMIT_KPI)} calculateApplicableWeight={calculateApplicableWeight} calculateKpiResultScoreCompliance={calculateKpiResultScoreCompliance} registerLocation={registerLocation} jsonLocation={jsonLocation} uiConfiguration={uiConfiguration} />
</Col>
<Col md={12}>
<DenominatorInput disableFields={disableFields || (!useFormMethods.getValues(`${registerLocation}.comments`) && taskName !== TASK_NAME.SUBMIT_KPI)} registerLocation={registerLocation} jsonLocation={jsonLocation} uiConfiguration={uiConfiguration} />
</Col>
</FormProvider>
</td>
<td className='align-middle'>
<Col md={12}>
<p>{bcmKpi?.numeratorDescription}</p>
</Col>
<Col md={12}>
<p>{bcmKpi?.denominatorDescription}</p>
</Col>
</td>
<td className='align-middle text-center'>
{useFormMethods.getValues(`${registerLocation}.kpiResult`)}
</td>
<td className='align-middle text-center'>
{bcmKpi?.performanceIndicatorValue?.score}
</td>
<td className='align-middle text-center' style={{ backgroundColor: getColorForCompliance(bcmKpi?.performanceIndicatorValue?.compliance as string) }}>
{bcmKpi?.performanceIndicatorValue?.compliance}
</td>
<td className='align-middle text-center max-w-25 min-w-25' >
<Attachments
setIsUploadedRequiredAttachments={setIsUploadedRequiredAttachments}
isFieldAttachments={true}
source={DOE_SOURCE}
type={BCM_REPORT_TYPE}
attachmentID={ATTACHMENT_CONFIG.KPI_REPORT}
files={useFormMethods.getValues(`${registerLocation}.attachments`) || []}
requestId={useFormMethods.getValues(`${registerLocation}.attachments`)[0]?.requestId}
readOnly={disableFields || (!useFormMethods.getValues(`${registerLocation}.comments`) && taskName !== TASK_NAME.SUBMIT_KPI)}
/>
<small>
<ErrorMessage
className="text-danger error-message"
errors={useFormMethods.formState.errors}
name={`attachments`}
as="p"
/>
</small>
</td>
<td className='align-middle text-center w-25'>
<Controller
name={`${registerLocation}.justification`}
control={useFormMethods.control}
render={({ field: { onChange, value } }) => (
<>
<Form.Control id={(`${registerLocation}`).toString()} disabled={disableFields || (!useFormMethods.getValues(`${registerLocation}.comments`) && taskName !== TASK_NAME.SUBMIT_KPI)} value={value || null} type="text" as="textarea" rows={3} className={`form-control .${useFormMethods.getFieldState(`${registerLocation}.justification`).error ? 'is-invalid' : ''}`} placeholder={uiConfiguration?.UI_LABELS?.JUSTIFICATION || 'Justification'} onChange={onChange} />
<small>
<ErrorMessage
className="text-danger error-message"
errors={useFormMethods.formState.errors}
name={`${registerLocation}.justification`}
as="p"
/>
</small>
</>
)} />
</td>
{ (userInfo?.role?.split(",")?.includes(COMMON_ROLES.BCM_REVIEWER) || taskName === TASK_NAME.BCM_MORE_INFO_BY_REVIEWER )&& <td className='align-middle text-center w-25'>
<Controller
name={`${registerLocation}.comments`}
control={useFormMethods.control}
render={({ field: { onChange, value } }) => (
<>
<Form.Control id={(`${registerLocation}`).toString()} disabled={!userInfo?.role?.split(",")?.includes(COMMON_ROLES.BCM_REVIEWER)} value={value || null} type="text" as="textarea" rows={3} className={`form-control .${useFormMethods.getFieldState(`${registerLocation}.comments`).error ? 'is-invalid' : ''}`} placeholder={uiConfiguration?.UI_LABELS?.COMMENTS || 'Comments'} onChange={onChange} />
<small>
<ErrorMessage
className="text-danger error-message"
errors={useFormMethods.formState.errors}
name={`${registerLocation}.comments`}
as="p"
/>
</small>
</>
)} />
</td>}
<td colSpan={12} ></td>
</tr>
</React.Fragment>
)
}
// NumeratorAndDenominator Related Code Start
interface INumeratorAndDenominatorInput {
calculateKpiResultScoreCompliance?: any,
registerLocation: any,
jsonLocation: any,
uiConfiguration: IUiConfigurations,
calculateApplicableWeight?: any,
disableFields: boolean
}
const NumeratorInput = ({ calculateKpiResultScoreCompliance, registerLocation, jsonLocation, uiConfiguration, calculateApplicableWeight, disableFields }: INumeratorAndDenominatorInput) => {
const useFormMethods = useFormContext();
const watchNumerator = useFormMethods.watch(`${registerLocation}.numerator`);
const watchDenominator = useFormMethods.watch(`${registerLocation}.denominator`);
const watchNotApplicableValue = useFormMethods.watch(`${registerLocation}.notApplicable`);
// Calculating KpiResult Score Compliance and Total
useEffect(() => {
checkNumeratorandDenominatorValidation(registerLocation)
calculateKpiResultScoreCompliance(`${jsonLocation}`);
}, [watchNumerator, watchDenominator]);
const checkNumeratorandDenominatorValidation = (registerLocation: any) => {
const numerator = parseFloat(watchNumerator); // Parse as float if needed
const denominator = parseFloat(watchDenominator); // Parse as float if needed
if (denominator == 0) {
// Set an error message for the Denominator field
useFormMethods.setError(`${registerLocation}.denominator`, {
type: 'manual',
message: 'Denominator cannot be zero',
});
} else if (numerator > denominator) {
// Set an error message for the Numerator field
useFormMethods.setError(`${registerLocation}.numerator`, {
type: 'manual',
message: 'Numerator cannot be greater than Denominator',
});
}
else {
// Clear errors if conditions are met
useFormMethods.clearErrors(`${registerLocation}.denominator`);
useFormMethods.clearErrors(`${registerLocation}.numerator`);
}
useFormMethods.trigger(`${registerLocation}.denominator`);
useFormMethods.trigger(`${registerLocation}.numerator`);
console.log(useFormMethods.formState.errors, useFormMethods.getFieldState(`${registerLocation}.numerator`).error, useFormMethods.formState.isValid)
}
return (
<>
<Controller key={`${registerLocation}.numerator`}
name={`${registerLocation}.numerator`}
control={useFormMethods.control}
render={({ field: { onChange, value } }) => (
<>
<Form.Control id={(`${registerLocation}`).toString()} value={value || ''} disabled={watchNotApplicableValue || disableFields} type="number" className={`form-control .${useFormMethods.getFieldState(`${registerLocation}.numerator`).error ? 'is-invalid' : ''}`} placeholder={uiConfiguration?.UI_LABELS?.NUMERATOR || 'Numerator'} onChange={(e) => { e.target.value = restrictToTwoDecimals(e.target.value); onChange(e.target.value); }} />
<small>
<ErrorMessage
className="text-danger error-message"
errors={useFormMethods.formState.errors}
name={`${registerLocation}.numerator`}
as="p"
/>
</small>
</>
)} />
</>
)
}
const DenominatorInput = ({ registerLocation, uiConfiguration, disableFields }: INumeratorAndDenominatorInput) => {
const useFormMethods = useFormContext();
const watchNotApplicableValue = useFormMethods.watch(`${registerLocation}.notApplicable`);
return (
<>
<Controller key={`${registerLocation}.denominator`}
name={`${registerLocation}.denominator`}
control={useFormMethods.control}
render={({ field: { onChange, value } }) => (
<>
<Form.Control id={(`${registerLocation}`).toString()} value={value || ''} disabled={watchNotApplicableValue || disableFields} type="number" className={`form-control .${useFormMethods.getFieldState(`${registerLocation}.denominator`).error ? 'is-invalid' : ''}`} placeholder={uiConfiguration?.UI_LABELS?.DENOMINATOR || 'Denominator'} onChange={(e) => { e.target.value = restrictToTwoDecimals(e.target.value); onChange(e.target.value); }} />
<small>
<ErrorMessage
className="text-danger error-message"
errors={useFormMethods.formState.errors}
name={`${registerLocation}.denominator`}
as="p"
/>
</small>
</>
)} />
</>
)
}
// NumeratorAndDenominator Related Code End