Полное руководство по таймаутам действий Redux
Узнайте, как отправлять действия Redux с таймаутами для автоматического скрытия уведомлений. Полное руководство с примерами для разработчиков React, использующих Redux, Redux Toolkit и решения с промежуточным программным обеспечением.
Как отправить Redux действие с таймаутом?
У меня есть действие, которое обновляет состояние уведомлений моего приложения. Обычно это уведомление будет какого-либо типа ошибки или информации. Мне нужно затем отправить другое действие через 5 секунд, которое вернет состояние уведомлений в исходное, то есть уберет уведомление. Основная причина этого - предоставить функциональность, при которой уведомления автоматически исчезают через 5 секунд.
У меня не получилось использовать setTimeout для возврата другого действия, и я не могу найти, как это сделать в интернете. Поэтому буду рад любым советам.
Отправка Redux-экшена с таймаутом для автоматического скрытия уведомлений
Для отправки Redux-экшена с таймаутом для автоматического скрытия уведомлений необходимо использовать функцию setTimeout() JavaScript в сочетании с механизмом диспетчеризации Redux. Прямой подход заключается в обертке второго вызова dispatch() в функции setTimeout(), но существует несколько способов реализации в зависимости от вашей настройки Redux.
Содержание
- Базовый подход с setTimeout
- Реализация на основе компонентов
- Решение с Redux-thunk
- Реализация с Redux Toolkit
- Расширенное управление таймаутами
- Лучшие практики
- Пример полной системы уведомлений
Базовый подход с setTimeout
Самый простой способ реализации задержанной диспетчеризации - использование setTimeout() непосредственно в вашем создателе экшенов или компоненте. Вот базовый пример:
// Создатели экшенов
export const showNotification = (message, type = 'info') => ({
type: 'SHOW_NOTIFICATION',
payload: { message, type }
});
export const hideNotification = () => ({
type: 'HIDE_NOTIFICATION'
});
// Экшен с таймаутом
export const showNotificationWithTimeout = (message, type = 'info') => {
return (dispatch) => {
// Показать уведомление немедленно
dispatch(showNotification(message, type));
// Скрыть через 5 секунд
setTimeout(() => {
dispatch(hideNotification());
}, 5000);
};
};
Этот подход работает, потому что вы возвращаете функцию, которая получает dispatch в качестве параметра, что возможно при использовании промежуточного ПО redux-thunk.
Реализация на основе компонентов
Если вы предпочитаете обрабатывать логику таймаута внутри вашего React-компонента, вы можете использовать хук useEffect:
import { useDispatch } from 'react-redux';
import { showNotification, hideNotification } from '../actions/notificationActions';
const NotificationComponent = ({ message, type }) => {
const dispatch = useDispatch();
useEffect(() => {
// Настроить таймаут для скрытия уведомления
const timer = setTimeout(() => {
dispatch(hideNotification());
}, 5000);
// Функция очистки для отмены таймаута при размонтировании компонента
return () => clearTimeout(timer);
}, [dispatch, message, type]);
return (
<div className={`notification ${type}`}>
{message}
</div>
);
};
Этот подход чистый и автоматически обрабатывает очистку при размонтировании компонента.
Решение с Redux-thunk
Для более сложной асинхронной логики redux-thunk предоставляет надежное решение. Сначала убедитесь, что у вас установлен redux-thunk:
npm install redux-thunk
Затем настройте ваш стор с промежуточным ПО thunk:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
Теперь вы можете создавать более сложные экшены с таймаутами:
// Асинхронный экшен с таймаутом
export const showNotificationWithAutoHide = (message, type = 'info', duration = 5000) => {
return async (dispatch) => {
// Диспатчить экшен показа уведомления
dispatch({
type: 'SHOW_NOTIFICATION',
payload: { message, type }
});
// Возвращаем промис, который разрешится после таймаута
return new Promise((resolve) => {
setTimeout(() => {
dispatch({
type: 'HIDE_NOTIFICATION'
});
resolve();
}, duration);
});
};
};
// Использование в компоненте
const handleButtonClick = () => {
dispatch(showNotificationWithAutoHide('Операция завершена!', 'success'))
.then(() => {
// Опционально: сделать что-то после скрытия уведомления
console.log('Уведомление было скрыто');
});
};
Реализация с Redux Toolkit
Redux Toolkit упрощает этот процесс с встроенной поддержкой асинхронных экшенов. Вот как это реализовать:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface NotificationState {
open: boolean;
message: string;
type: 'success' | 'error' | 'info' | 'warning';
}
const initialState: NotificationState = {
open: false,
message: '',
type: 'info'
};
const notificationSlice = createSlice({
name: 'notification',
initialState,
reducers: {
showNotification: (state, action: PayloadAction<{ message: string; type: string }>) => {
state.open = true;
state.message = action.payload.message;
state.type = action.payload.type;
},
hideNotification: (state) => {
state.open = false;
state.message = '';
state.type = 'info';
}
}
});
export const { showNotification, hideNotification } = notificationSlice.actions;
// Асинхронный создатель экшена
export const showNotificationWithTimeout = (message: string, type: string = 'info') => {
return (dispatch: any) => {
dispatch(showNotification({ message, type }));
setTimeout(() => {
dispatch(hideNotification());
}, 5000);
};
};
export default notificationSlice.reducer;
Расширенное управление таймаутами
Для более сложного управления таймаутами вы можете использовать библиотеку redux-timeout, специально разработанную для этой цели:
npm install redux-timeout
Вот как ее настроить:
import { createStore, applyMiddleware } from 'redux';
import timeout from 'redux-timeout';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(timeout()));
Затем вы можете создавать экшены с расширенными возможностями таймаута:
// Экшен, который вызовет таймаут
export const SHOW_NOTIFICATION = 'SHOW_NOTIFICATION';
export const HIDE_NOTIFICATION = 'HIDE_NOTIFICATION';
// Это скроет уведомление, если оно отображалось 5 секунд
export const scheduleNotificationHide = (notificationId) => ({
type: 'HIDE_NOTIFICATION_IF_TIMEOUT',
payload: { notificationId },
meta: {
timeout: 5000,
action: HIDE_NOTIFICATION
}
});
Лучшие практики
При реализации экшенов на основе таймаутов в Redux учитывайте эти лучшие практики:
-
Всегда очищайте таймауты: Используйте
clearTimeout()в функциях очисткиuseEffectкомпонентов для предотвращения утечек памяти. -
Храните ссылки на таймауты: Если вам нужно динамически отменять таймауты, храните ID таймаута в вашем состоянии:
const [timeoutId, setTimeoutId] = useState(null);
useEffect(() => {
const id = setTimeout(() => {
dispatch(hideNotification());
}, 5000);
setTimeoutId(id);
return () => clearTimeout(id);
}, [dispatch]);
-
Обрабатывайте несколько уведомлений: Если вы можете показывать несколько уведомлений одновременно, используйте уникальные ID для отслеживания, к какому уведомлению относится каждый таймаут.
-
Рассмотрите дебаунсинг: Для частых уведомлений реализуйте дебаунсинг, чтобы предотвратить конфликты нескольких таймаутов.
-
Используйте промежуточное ПО для согласованности: Для сложных приложений рассмотрите использование специализированного промежуточного ПО, такого как
redux-sagaилиredux-observable, для управления таймаутами.
Пример полной системы уведомлений
Вот полный пример реализации системы уведомлений с использованием Redux Toolkit:
// store.js
import { configureStore } from '@reduxjs/toolkit';
import notificationReducer from './features/notificationSlice';
export const store = configureStore({
reducer: {
notification: notificationReducer
}
});
// features/notificationSlice.js
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface Notification {
id: string;
message: string;
type: 'success' | 'error' | 'info' | 'warning';
open: boolean;
}
const initialState: Notification[] = [];
const notificationSlice = createSlice({
name: 'notification',
initialState,
reducers: {
addNotification: (state, action: PayloadAction<Omit<Notification, 'id' | 'open'>>) => {
const newNotification = {
...action.payload,
id: Math.random().toString(36).substr(2, 9),
open: true
};
state.push(newNotification);
// Автоматически скрыть через 5 секунд
setTimeout(() => {
const notification = state.find(n => n.id === newNotification.id);
if (notification) {
notification.open = false;
}
}, 5000);
},
removeNotification: (state, action: PayloadAction<string>) => {
return state.filter(notification => notification.id !== action.payload);
}
}
});
export const { addNotification, removeNotification } = notificationSlice.actions;
export default notificationSlice.reducer;
// components/Notification.js
import { useSelector, useDispatch } from 'react-redux';
import { removeNotification } from '../store/features/notificationSlice';
const Notification = () => {
const notifications = useSelector((state) => state.notification);
const dispatch = useDispatch();
return (
<div className="notification-container">
{notifications.map((notification) => (
<div
key={notification.id}
className={`notification ${notification.type} ${notification.open ? 'open' : 'closing'}`}
>
{notification.message}
<button onClick={() => dispatch(removeNotification(notification.id))}>
×
</button>
</div>
))}
</div>
);
};
export default Notification;
// Использование
const Component = () => {
const dispatch = useDispatch();
const showError = () => {
dispatch(addNotification({
message: 'Произошла ошибка!',
type: 'error'
}));
};
return (
<button onClick={showError}>Показать ошибку</button>
);
};
Этот полный пример демонстрирует надежную систему уведомлений с автоматическим скрытием на основе таймаута, правильной очисткой и опциями ручного скрытия.
Ключевое понимание заключается в том, что Redux-экшены с таймаутами не являются чем-то особенным - это просто JavaScript-функции, которые используют setTimeout() для задержки последующих вызовов dispatch(). Этот паттерн работает, потому что промежуточное ПО Redux, такое как redux-thunk, позволяет создателям экшенов возвращать функции, которые получают dispatch в качестве параметра, что дает вам возможность контролировать, когда и как диспатчить экшены.
Источники
- How to dispatch a Redux action with a timeout? - Stack Overflow
- How to fire periodic actions using setTimeout and dispatcher in redux - Pluralsight
- A Simple Way to Implement Timers and Timeouts in Redux - Atomic Object
- Create a robust notification system for your React App using MUI and Redux Toolkit - Intertech
- GitHub - gpfunk/redux-timeout: Watches and triggers actions if a timeout threshold is met
- How to dispatch a Redux action with a timeout? - Atomized Objects
- React: Dispatching A Redux Action With Timeout - Medium
Заключение
Отправка Redux-экшенов с таймаутами становится простой, как только вы понимаете основные паттерны:
- Самый простой подход использует
setTimeout()непосредственно внутри создателей экшенов или компонентов - Реализации на основе компонентов с
useEffectобеспечивают автоматическую очистку - Redux-thunk позволяет реализовать сложную асинхронную логику с таймаутами
- Redux Toolkit упрощает процесс с встроенной поддержкой асинхронных экшенов
- Расширенные библиотеки, такие как
redux-timeout, предлагают специализированное управление таймаутами
Для вашего случая с уведомлениями наиболее практичным решением является:
- Использование
setTimeout()внутри вашего создателя экшена уведомлений для отправки экшена скрытия через 5 секунд - Реализация логики таймаута в вашем React-компоненте с использованием
useEffectс правильной очисткой
Всегда очищайте таймауты для предотвращения утечек памяти, особенно в реализациях на основе компонентов. Паттерн работает, потому что промежуточное ПО Redux позволяет создателям экшенов быть функциями, а не простыми объектами, что дает вам гибкость контроля над тем, когда и как диспатчить экшены.
Если вам нужно более сложное управление таймаутами или вы хотите обрабатывать крайние случаи, такие как несколько одновременных уведомлений, рассмотрите использование специализированного промежуточного ПО или реализацию более надежной системы уведомлений с уникальными ID и централизованным управлением таймаутами.