REACT:错误:渲染的钩子比预期少。这可能是由于

Des*_*amo 1 javascript reactjs react-hooks

我有一个问题,我有著名的反应错误:未捕获(承诺)错误:渲染的钩子比预期少。这可能是由于意外的提前退货声明造成的。

\n

但我正在寻找问题,但没有找到,因为我认为我的钩子顺序很好,但事实并非如此......您知道错误在哪里吗?

\n

谢谢

\n
import { useNavigate, Link, useParams } from 'react-router-dom';\n    import { useEffect, useState, useCallback, useMemo } from 'react';\n    import { Formik } from 'formik';\n    import { useIntl, FormattedMessage } from 'react-intl';\n    import { useQuery, gql, useMutation } from '@apollo/client';\n    import { sub } from 'date-fns';\n    \n    import {\n      Text,\n      Button,\n      TextField,\n      DateField,\n      Banner,\n      Loader,\n      Icon,\n    } from '@customer-portal/components';\n    \n    import { StandardField } from '../../components/FormikFields';\n    import { useError } from '../../hooks/useError';\n    import { toISODate, toStartOfDay } from '../../utils/date';\n    import { useFilteredContracts } from '../../components/ContractsFilter';\n    \n    export function MeterReadingToEnterForm() {\n      const [date, setDate] = useState();\n      const [forceResult, setForceResult] = useState(false);\n      const [successBanner, setSuccessBanner] = useState(false);\n      let navigate = useNavigate();\n      let { serial } = useParams();\n      const intl = useIntl();\n      const contract = useFilteredContracts();\n      const initialValues = {};\n      const getError = useError(error);\n    \n      useEffect(() => {\n        return () => {\n          navigate('/meter-reading');\n        };\n      }, [contract.id]);\n    \n      const {\n        loading,\n        data: queryData,\n        error,\n        refetch,\n      } = useQuery(QUERY, {\n        notifyOnNetworkStatusChange: true,\n        fetchPolicy: 'network-only',\n        variables: {\n          date: toStartOfDay(Date.now()),\n          contractId: contract.id,\n        },\n      });\n    \n      const [enterMeterReading, { loading: enterLoading, error: enterError }] =\n        useMutation(MUTATION_QUERY, {\n          onCompleted: () => setSuccessBanner(true),\n        });\n    \n      useEffect(() => {\n        refetch({ variables: { date: toStartOfDay(date) } });\n      }, [date]);\n    \n      if (error) {\n        return (\n          <Banner data-test="BannerNoContracts" type="error" iconName="Alert">\n            <Text>{getError()}</Text>\n          </Banner>\n        );\n      }\n    \n      if (enterError) {\n        return (\n          <Banner type="error" iconName="Alert">\n            <Text>{getError()}</Text>\n          </Banner>\n        );\n      }\n    \n      const formattedValues = (valeur) => {\n        let array = Object.keys(valeur).map((key) => ({\n          id: key,\n          result: valeur[key],\n        }));\n        return array;\n      };\n    \n      const handle = useCallback((values) => {\n        mutationQuery({\n          variables: {\n            data: {\n              serial: serial,\n              results: formattedValues(values),\n            },\n            Id: id,\n          },\n         refetchQueries: ['newQuery'],\n        });\n      });\n    \n      return (\n        <div sx={{ display: 'flex', flexDirection: 'column', width: '100%', p: 2 }}>\n          <div sx={{ display: 'flex', pt: 6, width: '100%' }}>\n            <Icon\n              sx={{ display: 'flex', alignItems: 'center', m: 3 }}\n              color="primary"\n              name="Counter"\n              size="large"\n            />\n            <Text weight="bold" sx={{ display: 'flex', alignItems: 'center' }}>\n              <FormattedMessage\n                defaultMessage="N\xc2\xb0 {number}"\n                values={{\n                  number: `${serial}`,\n                }}\n              />\n            </Text>\n          </div>\n          {successBanner && (\n            <div sx={{ mb: 4 }}>\n              <Banner\n                iconName="SuccessOutline"\n                type="success"\n              >\n                <Text>\n                  <FormattedMessage\n                    defaultMessage="Done"\n                  />\n                </Text>\n              </Banner>\n            </div>\n          )}\n          <div sx={{ display: 'flex', justifyContent: 'flex-start', py: 2 }}>\n            <DateField\n              name="datefoield"\n              value={date || Date.now()}\n              onChange={(date) => setDate(date)}\n              required={true}\n              label={intl.formatMessage({\n                defaultMessage: 'Date du relev\xc3\xa9',\n              })}\n              defaultSelected={date}\n              disabledDays={[\n                {\n                  before: sub(new Date(toISODate(Date.now())), {\n                    days: 60,\n                  }),\n                },\n                {\n                  after: new Date(Date.now()),\n                },\n              ]}\n            />\n          </div>\n          {enterLoading && <Loader type="radiance" overlay={true} />}\n          {loading ? (\n            <Loader sx={{ mx: [0, 11] }} />\n          ) : (\n            <Formik\n              onSubmit={handleEnterMeterReading}\n              initialValues={initialValues}\n            >\n              {({ values, handleSubmit }) => {\n                const query = useMemo(() => {\n                  return queryDatafind(\n                    (serialNumber) => serialNumber === serial\n                  );\n                }, [queryData]); \n    \n                return (\n                  <form\n                    noValidate={true}\n                    onSubmit={handleSubmit}\n                  >\n                    <div\n                      sx={{\n                        display: 'flex',\n                        flexWrap: 'wrap',\n                        width: '100%',\n                        py: 2,\n                      }}\n                    >\n                      {query.map((value) => (\n                        <div sx={{ mr: 4 }}>\n                          <StandardField\n                            as={TextField}\n                            id={id}\n                            label={label}\n                            required\n                            name={id}\n                            size="standard"\n                            type="number"\n                            variant="standard"\n                          />\n                        </div>\n                      ))}\n                    </div>\n                    <div sx={{ display: 'flex', py: 6 }}>\n                      <Link to="/">\n                        <Button\n                          sx={{ my: 4, mr: 4 }}\n                          startIcon="ErrorOutline"\n                          size="standard"\n                          variant="outlined"\n                        >\n                          {intl.formatMessage({\n                            defaultMessage: 'Annuler',\n                          })}\n                        </Button>\n                      </Link>\n                      <Button\n                        sx={{ whiteSpace: 'nowrap', my: 4 }}\n                        startIcon="SuccessOutline"\n                        size="standard"\n                        type="submit"\n                        disabled={loading}\n                      >\n                        {loading\n                          ? intl.formatMessage({\n                              defaultMessage: 'loading',\n                            })\n                          : intl.formatMessage({\n                              defaultMessage: 'Validate',\n                            })}\n                      </Button>\n                    </div>\n                  </form>\n                );\n              }}\n            </Formik>\n          )}\n        </div>\n      );\n    }\n    \n\n-- GRAPHQL REQUEST---\n
Run Code Online (Sandbox Code Playgroud)\n

ede*_*ine 6

问题是您useCallback在某些if条件之后调用可能会return过早。您可以删除该useCallback调用(只需handle在每次渲染时设置一个新的函数闭包),或者将调用移至suseCallback之上if ... return