Реагирует — Ошибка: превышена максимальная глубина обновления — useReducer

#reactjs #typescript

#reactjs #typescript

Вопрос:

Я столкнулся с ошибкой:

react-dom.development.js:23093 Ошибка без обнаружения: превышена максимальная глубина обновления. Это может произойти, когда компонент повторно вызывает setState внутри componentWillUpdate или componentDidUpdate. React ограничивает количество вложенных обновлений, чтобы предотвратить бесконечные циклы.

Я понимаю, что проблема может быть связана с тем, что я вызываю функции checkError и validationPassed, которые изменяют состояние через useReducer, в функции checkValidations, которая вызывается через перехват useEffect, но я не знаю, как это решить

Код выглядит так:

 interface ValidationField {
  errorMessage?: string;
  focused: boolean;
  hasError: boolean;
}

interface ClientEditorState {
  client: Client;
  validations: { [key in keyof Client]: ValidationField };
}

enum clientEditorActions {
  UPDATE_ENTITY = 'CLIENT_EDITOR/UPDATE_ENTITY',
  UPDATE_FOCUSED = 'CLIENT_EDITOR/UPDATE_FOCUSED',
  VALIDATION_ERROR = 'CLIENT_EDITOR/VALIDATION_ERROR',
  VALIDATION_PASSED = 'CLIENT_EDITOR/VALIDATION_PASSED',
}

interface UpdateEntityAction extends Action<typeof clientEditorActions.UPDATE_ENTITY> {
  name: string;
  value: string | boolean;
}
interface UpdateFocusedAction extends Action<typeof clientEditorActions.UPDATE_FOCUSED> {
  name: string;
}
interface ValidationErrorAction extends Action<typeof clientEditorActions.VALIDATION_ERROR> {
  message: string;
  name: string;
}
interface ValidationPassedAction extends Action<typeof clientEditorActions.VALIDATION_PASSED> {
  name: string;
}

type ClientEditorActions = UpdateEntityAction | UpdateFocusedAction | ValidationErrorAction | ValidationPassedAction;

const clientReducer: Reducer<ClientEditorState, ClientEditorActions> = (prevState, action) => {
  switch (action.type) {
    case clientEditorActions.UPDATE_ENTITY:
      const clientUpdated = _cloneDeep(prevState || ({} as Client));
      _set(clientUpdated, `client.${action.name}`, action.value);
      return clientUpdated;
    case clientEditorActions.UPDATE_FOCUSED:
      const validationField = _cloneDeep(prevState);
      _set(validationField, `validations.${action.name}.focused`, true);
      return validationField;
    case clientEditorActions.VALIDATION_ERROR:
      const errorField = _cloneDeep(prevState);
      _set(errorField, `validations.${action.name}.hasError`, true);
      _set(errorField, `validations.${action.name}.errorMessage`, action.message);
      return errorField;
    case clientEditorActions.VALIDATION_PASSED:
      const passed = _cloneDeep(prevState);
      _set(passed, `validations.${action.name}.hasError`, false);
      _set(passed, `validations.${action.name}.errorMessage`, undefined);
      return passed;
    default:
      return prevState;
  }
};

...

const getInitialState = (): ClientEditorState => ({
    client: entity as Client,
    validations: {
      firstName: {
        focused: false,
        hasError: false,
      },
     
    },
  });
  const [state, clientDispatch] = useReducer(clientReducer, getInitialState());

  const checkError = useCallback((name: string, message: string) => {
    clientDispatch({
      type: clientEditorActions.VALIDATION_ERROR,
      name,
      message,
    });
  }, []);

  const validationPassed = useCallback((name: string) => {
    clientDispatch({
      type: clientEditorActions.VALIDATION_PASSED,
      name,
    });
  }, []);

const checkValidations = useCallback(
    (c: Client) => {
      let validation = false;

      const { firstName } = state.validations;

      if (!c.firstName amp;amp; firstName.focused) {
        validation = false;
        checkError('firstName', f('client.requiredFieldClient'));
      } else {
        validation = true;
        validationPassed('firstName');
      }

    
    },
    [checkError, f, state.validations, validationPassed],
  );

  const [clientUpdateHandler] = useDebouncedCallback((clientUpdated: Client) => {
    dispatch(updateEntityEditor(clientUpdated));
  }, 800);

  useEffect(() => {
    if (!_isEqual(state.client, entity)) {
      clientUpdateHandler(state.client as Client);
    }
    const { firstName } = state.validations;

    if (firstName.focused) checkValidations(state.client);
  }, [checkValidations, clientUpdateHandler, entity, state.client, state.validations]);
  

Ответ №1:

Я понимаю, что проблема может быть связана с тем, что я вызываю функции checkError и validationPassed, которые изменяют состояние через пользовательский редактор, в функции checkValidations, которая вызывается через перехват useEffect

Да, точно. Попробуйте уменьшить ваши зависимости в useEffect и связанных функциях в useCallback. Любое из этих изменений приведет к повторному запуску useEffect.

Возвращаясь к вопросу, ваш эффект использования в настоящее время зависит от состояния.проверки. Поэтому всякий раз, когда state.validations изменяется, useEffect запускается повторно. Рассмотрите возможность выполнения

 const { firstName } = state.validations;
  

вне useEffect и обратного вызова checkValidations. Это остановит его повторный запуск при каждом изменении state.validations.