UNPKG

2.75 kBJavaScriptView Raw
1import React, { createContext, useContext, useReducer } from 'react';
2import withLogger from '../util/withLogger';
3
4/**
5 * The current state of the toast store.
6 *
7 * @typedef {Object} ToastState
8 *
9 * @property {Map} toasts Map object associating an id to toast data
10 */
11const initialState = {
12 toasts: new Map()
13};
14
15const reducer = (prevState = initialState, action = {}) => {
16 const { type, payload } = action;
17
18 switch (type) {
19 case 'add': {
20 const nextToasts = new Map(prevState.toasts);
21 const prevToast = nextToasts.get(payload.id);
22
23 const isDuplicate = !!prevToast;
24 let timestamp = payload.timestamp;
25 if (isDuplicate) {
26 // If this is a _new_ duplicate toast we need to clear the
27 // previous timeout to prevent premature removal.
28 window.clearTimeout(prevToast.removalTimeoutId);
29
30 // And to retain chronological order of addition, keep the
31 // original timestamp.
32 timestamp = prevToast.timestamp;
33 }
34
35 nextToasts.set(payload.id, {
36 ...payload,
37 timestamp,
38 isDuplicate
39 });
40
41 return {
42 ...prevState,
43 toasts: nextToasts
44 };
45 }
46 case 'remove': {
47 const nextToasts = new Map(prevState.toasts);
48
49 const prevToast = nextToasts.get(payload.id);
50 if (prevToast) {
51 window.clearTimeout(prevToast.removalTimeoutId);
52 }
53
54 nextToasts.delete(payload.id);
55
56 return {
57 ...prevState,
58 toasts: nextToasts
59 };
60 }
61 default:
62 return prevState;
63 }
64};
65
66const ToastContext = createContext();
67
68const wrappedReducer = withLogger(reducer);
69
70/**
71 * A [context]{@link https://reactjs.org/docs/context.html} provider that
72 * provides the toast state object and a dispatch function to toast
73 * functionality consumers.
74 *
75 * @typedef ToastContextProvider
76 *
77 */
78export const ToastContextProvider = ({ children }) => {
79 const store = useReducer(wrappedReducer, initialState);
80 return (
81 <ToastContext.Provider value={store}>{children}</ToastContext.Provider>
82 );
83};
84
85/**
86 * A hook that provides access to the toast state and dispatch.
87 * Any component using this hook _must_ be a child of a {@link ToastContextProvider}.
88 *
89 * @typedef useToastContext
90 *
91 * @return {Object[]} An array containing the state and dispatch function: [{@link ToastState}, function]
92 *
93 * @example
94 * const [toastState, dispatch] = useToastState();
95 */
96export const useToastContext = () => useContext(ToastContext);