Advanced Redux — Side Effects, Async Tasks & ThunksEntry 3 of 9
A UI Slice for Notifications
A separate slice that stores UI state, such as a notification banner showing the status of an HTTP request.
Notes
- HTTP status (pending, success, error) is app-wide UI state, so it fits in its own
uislice next tocart. - One
showNotificationaction takesstatus,title, andmessage, and any component or thunk can dispatch it. - A
Notificationcomponent readsstate.ui.notificationand renders when it's notnull.
What / Why / When
- What
Global UI feedback stored in Redux.
- Why
Side-effect code (thunks, components) can report status without prop drilling.
- When
Status messages, loading banners, toggles shared across the app.
Code compare
// store/ui-slice.tsimport { createSlice, type PayloadAction } from "@reduxjs/toolkit";type Notification = { status: "pending" | "success" | "error"; title: string; message: string;};type UiState = { cartIsVisible: boolean; notification: Notification | null };const initialState: UiState = { cartIsVisible: false, notification: null };const uiSlice = createSlice({ name: "ui", initialState, reducers: { toggleCart(state) { state.cartIsVisible = !state.cartIsVisible; }, showNotification(state, action: PayloadAction<Notification>) { state.notification = action.payload; }, },});export const uiActions = uiSlice.actions;export default uiSlice.reducer;// App.tsxconst notification = useAppSelector((state) => state.ui.notification);return ( <> {notification && ( <Notification status={notification.status} title={notification.title} message={notification.message} /> )} <Layout>{/* ... */}</Layout> </>);// store/ui.actions.tsexport type Notification = { status: "pending" | "success" | "error"; title: string; message: string;};export const UiActions = createActionGroup({ source: "UI", events: { "Toggle Cart": emptyProps(), "Show Notification": props<Notification>(), },});// store/ui.feature.tstype UiState = { cartIsVisible: boolean; notification: Notification | null };export const uiFeature = createFeature({ name: "ui", reducer: createReducer( { cartIsVisible: false, notification: null } as UiState, on(UiActions.toggleCart, (state) => ({ ...state, cartIsVisible: !state.cartIsVisible })), on(UiActions.showNotification, (state, { status, title, message }) => ({ ...state, notification: { status, title, message }, })), ),});// app.component.ts@Component({ selector: "app-root", imports: [NotificationComponent, LayoutComponent], template: ` @if (notification(); as n) { <app-notification [status]="n.status" [title]="n.title" [message]="n.message" /> } <app-layout /> `,})export class AppComponent { private store = inject(Store); protected notification = this.store.selectSignal(uiFeature.selectNotification);}Quick reference
| Task | React | Angular |
|---|---|---|
| Define UI state | uiSlice | uiFeature |
| Show notification | dispatch(uiActions.showNotification({...})) | store.dispatch(UiActions.showNotification({...})) |
| Read it | useAppSelector(s => s.ui.notification) | selectSignal(uiFeature.selectNotification) |