开发者问题收集

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

2021-07-27
6770

我遇到了一个问题,我遇到了著名的 React 错误:未捕获(在 Promise 中)错误:渲染的钩子数量少于预期。这可能是由意外的早期返回语句引起的。

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

谢谢

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

-- GRAPHQL REQUEST---
1个回答

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

edemaine
2021-07-27