Как обработать два действия по кнопке?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Обработка двух действий по кнопке
Часто нужно выполнить несколько операций при клике на кнопку: отправить данные, закрыть модальное окно, обновить список. Есть несколько подходов в зависимости от контекста.
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>
);
}
Рекомендации
- Используй async/await - самый читаемый подход
- Обрабатывай ошибки - всегда используй try/catch
- Показывай состояние загрузки - disable кнопку во время выполнения
- Группируй независимые операции - Promise.all для параллельных действий
- Логируй процесс - помогает при отладке
- Разделяй логику - создавай переиспользуемые функции
Выбор подхода зависит от сложности: для простых случаев - обычная функция, для сложных - разделение на микро-функции с обработкой ошибок.