← Назад к вопросам

Как обработать два действия по кнопке?

2.0 Middle🔥 191 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обработка двух действий по кнопке

Часто нужно выполнить несколько операций при клике на кнопку: отправить данные, закрыть модальное окно, обновить список. Есть несколько подходов в зависимости от контекста.

1. Простое объединение в одной функции

Мост типичный способ - создать handler, который вызывает нужные функции последовательно:

// React пример
const handleSubmit = async (e) => {
  e.preventDefault();
  
  // Действие 1: отправить форму
  try {
    await api.post('/api/v1/questions', formData);
  } catch (error) {
    toast.error('Ошибка отправки');
    return; // Если ошибка - не идем дальше
  }
  
  // Действие 2: закрыть модаль
  closeModal();
  
  // Действие 3: обновить список
  refetchQuestions();
  
  // Действие 4: показать уведомление
  toast.success('Вопрос добавлен');
};

<form onSubmit={handleSubmit}>
  {/* форма */}
  <button type="submit">Сохранить</button>
</form>

Плюсы:

  • Простота
  • Полный контроль над порядком
  • Легко обработать ошибки

Минусы:

  • При большом количестве действий - много кода
  • Сложно переиспользовать

2. Разделение на микро-функции

Делим логику на мелкие функции, потом комбинируем:

const saveQuestion = async (data) => {
  return await api.post('/api/v1/questions', data);
};

const notifySuccess = (message) => {
  toast.success(message);
};

const updateQuestionsList = (onSuccess) => {
  refetchQuestions();
  onSuccess?.();
};

const handleClick = async () => {
  try {
    // Действие 1
    const result = await saveQuestion(formData);
    
    // Действие 2
    closeModal();
    
    // Действие 3 с callback
    updateQuestionsList(() => {
      // Действие 4
      notifySuccess('Успешно!');
    });
  } catch (error) {
    notifyError(error.message);
  }
};

<button onClick={handleClick}>Сохранить</button>

Плюсы:

  • Модульность
  • Переиспользуемость функций
  • Легче тестировать

Минусы:

  • Больше boilerplate кода

3. Promise Chain (цепочка)

Для асинхронных операций можно использовать .then():

const handleClick = () => {
  saveQuestion(formData)
    .then(() => {
      // Действие 2
      closeModal();
      return refetchQuestions();
    })
    .then(() => {
      // Действие 4
      toast.success('Успешно!');
    })
    .catch((error) => {
      toast.error(error.message);
    });
};

Плюсы:

  • Четкая последовательность
  • Естественный контроль потока

Минусы:

  • Может быть сложно читать (pyramid of doom)
  • Нужно быть осторожным с ошибками

4. Async/Await (современный подход)

Наиболее читаемый способ:

const handleClick = async () => {
  try {
    // Действие 1: сохранить
    await saveQuestion(formData);
    
    // Действие 2: закрыть
    closeModal();
    
    // Действие 3: обновить
    await refetchQuestions();
    
    // Действие 4: уведомить
    toast.success('Успешно!');
  } catch (error) {
    toast.error(`Ошибка: ${error.message}`);
  }
};

<button onClick={handleClick}>Сохранить</button>

Плюсы:

  • Очень читаемо
  • Как синхронный код, но асинхронный
  • Простая обработка ошибок

Минусы:

  • Нужны современные браузеры (но это стандарт сейчас)

5. Параллельные действия

Если действия не зависят друг от друга, можно выполнять одновременно:

const handleClick = async () => {
  try {
    // Действие 1: сохранить
    await saveQuestion(formData);
    
    // Действия 2 и 3 выполняются одновременно
    await Promise.all([
      closeModal(),
      refetchQuestions()
    ]);
    
    // Действие 4: уведомить
    toast.success('Успешно!');
  } catch (error) {
    toast.error(error.message);
  }
};

Плюсы:

  • Быстрее (параллельное выполнение)
  • Читаемо с Promise.all

Минусы:

  • Нужно быть уверенным, что действия независимы

6. Хук useCallback для React

Для оптимизации - сохраняем функцию и переиспользуем:

import { useCallback } from 'react';

function QuestionForm() {
  const handleSubmit = useCallback(async (e) => {
    e.preventDefault();
    
    try {
      // Действие 1
      await saveQuestion(formData);
      // Действие 2
      closeModal();
      // Действие 3
      await refetchQuestions();
      // Действие 4
      toast.success('Успешно!');
    } catch (error) {
      toast.error(error.message);
    }
  }, [formData]); // Зависимости
  
  return (
    <form onSubmit={handleSubmit}>
      {/* форма */}
      <button type="submit">Сохранить</button>
    </form>
  );
}

Плюсы:

  • Функция не пересоздается каждый рендер
  • Можно передавать как prop
  • Зависимости явные

7. Обработка ошибок между действиями

Иногда нужно остановить выполнение, если ошибка:

const handleClick = async () => {
  try {
    // Действие 1: критично
    const result = await saveQuestion(formData);
    if (!result) {
      throw new Error('Данные не сохранились');
    }
    
    // Действие 2: менее критично, но нужно
    try {
      await refetchQuestions();
    } catch (refetchError) {
      console.warn('Не смогли обновить список:', refetchError);
      // Не прерываем выполнение
    }
    
    // Действие 3: всегда выполнять (finally)
    closeModal();
    toast.success('Готово!');
  } catch (error) {
    toast.error(`Критическая ошибка: ${error.message}`);
  }
};

8. Пример: Реальный сценарий

// Компонент добавления вопроса
export function AddQuestionForm({ onSuccess }) {
  const [formData, setFormData] = useState({});
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    setError(null);
    
    try {
      // Действие 1: валидировать
      if (!formData.title) throw new Error('Заполните название');
      
      // Действие 2: отправить
      const response = await api.post('/api/v1/questions', formData);
      
      // Действие 3: очистить форму
      setFormData({});
      
      // Действие 4: уведомить родителя
      onSuccess?.(response);
      
      // Действие 5: показать сообщение
      toast.success('Вопрос добавлен!');
    } catch (err) {
      setError(err.message);
      toast.error(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input 
        value={formData.title || ''}
        onChange={(e) => setFormData({...formData, title: e.target.value})}
        placeholder="Название"
      />
      {error && <div className="error">{error}</div>}
      <button type="submit" disabled={loading}>
        {loading ? 'Сохранение...' : 'Сохранить'}
      </button>
    </form>
  );
}

Рекомендации

  1. Используй async/await - самый читаемый подход
  2. Обрабатывай ошибки - всегда используй try/catch
  3. Показывай состояние загрузки - disable кнопку во время выполнения
  4. Группируй независимые операции - Promise.all для параллельных действий
  5. Логируй процесс - помогает при отладке
  6. Разделяй логику - создавай переиспользуемые функции

Выбор подхода зависит от сложности: для простых случаев - обычная функция, для сложных - разделение на микро-функции с обработкой ошибок.

Как обработать два действия по кнопке? | PrepBro