Last active
May 4, 2022 15:59
-
-
Save deepakshrma/f37f1578ab7e6468459d6d329707f0cc to your computer and use it in GitHub Desktop.
Using Context and TypeScript to build an Alert Messenger in React.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// index.tsx | |
import { StrictMode } from "react"; | |
import ReactDOMClient from "react-dom/client"; | |
import { createGlobalStyle, ThemeProvider } from "styled-components"; | |
import App from "./App"; | |
import { AlertContextProvider } from "./components/Alert/AlertContextProvider"; | |
const Global = createGlobalStyle` | |
p, | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6 { | |
padding: 0; | |
margin: 0%; | |
} | |
`; | |
const rootElement = document.getElementById("root"); | |
const root = ReactDOMClient.createRoot(rootElement as HTMLElement); | |
const theme = { | |
colors: { | |
message_error: "#fdeded", | |
message_success: "#edf7ed", | |
}, | |
}; | |
root.render( | |
<StrictMode> | |
<ThemeProvider theme={theme}> | |
<AlertContextProvider> | |
<Global /> | |
<App /> | |
</AlertContextProvider> | |
</ThemeProvider> | |
</StrictMode> | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/AlertContextProvider.tsx | |
import { createContext, FC, ReactNode } from "react"; | |
interface AlertContext {} | |
export const AlertMessengerContext = createContext<AlertContext>({} as AlertContext); | |
export const AlertContextProvider: FC<{ | |
children: ReactNode; | |
noOfMessages?: number; | |
autoHideTimeout?: number; | |
autoHideError?: boolean; | |
}> = ({ children, noOfMessages = 5, autoHideTimeout = 3000, autoHideError = false }) => { | |
return( | |
<AlertMessengerContext.Provider value={{}}> | |
{children} | |
</AlertMessengerContext.Provider> | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/App.tsx | |
import { AppContainer, Button } from "./components/Alert/styled.components"; | |
function App() { | |
const onSuccess = () => {}; | |
const onError = () => {}; | |
return ( | |
<AppContainer> | |
<Button onClick={onSuccess} variant="success"> | |
Alert Success | |
</Button> | |
<Button onClick={onError} variant="error"> | |
Alert Error | |
</Button> | |
</AppContainer> | |
); | |
} | |
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/styled.components.tsx | |
import styled from "styled-components"; | |
export const AppContainer = styled.div` | |
display: flex; | |
justify-content: space-evenly; | |
`; | |
export const Button = styled.button<{ variant: string }>` | |
height: 48px; | |
width: 200px; | |
background-color: ${(p) => p.theme.colors[`message_${p.variant}`]}; | |
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//src/components/Alert/index.tsx | |
import { MouseEvent, ReactNode } from "react"; | |
import styled from "styled-components"; | |
enum MessageType { | |
SUCCESS = "success", | |
ERROR = "error", | |
} | |
interface AlertProps { | |
type?: MessageType; | |
title: string; | |
message?: string; | |
gutterBottom?: boolean; | |
gutterTop?: boolean; | |
actionIcon?: ReactNode | boolean; | |
onAction?: (e?: MouseEvent<HTMLButtonElement>) => void; | |
} | |
const noop = (_?: MouseEvent<HTMLButtonElement>) => {}; | |
const AlertContainer = styled.div<Partial<AlertProps>>` | |
padding: 10px 20px; | |
background: ${(p) => p.theme.colors[`message_${p.type}`]}; | |
margin-top: ${(p) => p.gutterTop && "10px"}; | |
margin-bottom: ${(p) => p.gutterBottom && "10px"}; | |
min-width: 300px; | |
height: 80px; | |
border-radius: 4px; | |
position: relative; | |
padding-top: 40px; | |
`; | |
const ActionIconContainer = styled.div` | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
`; | |
const Alert = ({ | |
type = MessageType.SUCCESS, | |
title, | |
message, | |
gutterBottom, | |
gutterTop, | |
actionIcon, | |
onAction, | |
}: AlertProps) => { | |
const ActionIcon = | |
typeof actionIcon === "boolean" && actionIcon ? <button onClick={onAction ?? noop}>X</button> : actionIcon; | |
return ( | |
<AlertContainer type={type} gutterBottom={gutterBottom} gutterTop={gutterTop}> | |
{actionIcon && <ActionIconContainer>{ActionIcon}</ActionIconContainer>} | |
<h4>{title}</h4> | |
<p>{message}</p> | |
</AlertContainer> | |
); | |
}; | |
export default Alert; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/AlertContextProvider.tsx | |
import { createContext, FC, ReactNode } from "react"; | |
import styled from "styled-components"; | |
import Alert from "."; | |
interface AlertContext {} | |
enum MessageType { | |
SUCCESS = "success", | |
ERROR = "error", | |
} | |
export const AlertMessengerContext = createContext<AlertContext>({} as AlertContext); | |
const messages = [ | |
{ | |
id: Date.now(), | |
type: MessageType.ERROR, | |
title: "Error Header", | |
message: "Error Message", | |
}, | |
{ | |
id: Date.now(), | |
type: MessageType.SUCCESS, | |
title: "Success Header", | |
message: "Success Message", | |
}, | |
]; | |
const AlertsContainer = styled.div` | |
background: #fff; | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
padding: 10px 20px; | |
overflow-y: auto; | |
max-height: calc(100vh - 100px); | |
`; | |
export const AlertContextProvider: FC<{ | |
children: ReactNode; | |
noOfMessages?: number; | |
autoHideTimeout?: number; | |
autoHideError?: boolean; | |
}> = ({ children, noOfMessages = 5, autoHideTimeout = 3000, autoHideError = false }) => { | |
return ( | |
<AlertMessengerContext.Provider value={{}}> | |
{children} | |
<AlertsContainer> | |
{messages.map(({ type, title, message, id }) => ( | |
<Alert | |
key={`alert__message__${id}`} | |
type={type} | |
title={title} | |
message={message} | |
gutterTop | |
actionIcon={type === "error"} | |
onAction={() => null} | |
/> | |
))} | |
</AlertsContainer> | |
</AlertMessengerContext.Provider> | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Message { | |
id: string; | |
type: MessageType; | |
title: string; | |
message?: string; | |
} | |
enum AlertActionKind { | |
ADD_SUCCESS = "ADD_SUCCESS", | |
ADD_ERROR = "ADD_ERROR", | |
REMOVE_MESSAGE = "REMOVE_MESSAGE", | |
} | |
interface AlertAction { | |
type: AlertActionKind; | |
payload?: any; | |
} | |
interface AlertState { | |
messages: Message[]; | |
noOfMessages: number; | |
} | |
const AlertReducer = (state: AlertState, action: AlertAction) => { | |
switch (action.type) { | |
case AlertActionKind.ADD_SUCCESS: | |
return { | |
...state, | |
messages: [{ type: "success", ...action.payload }, ...state.messages].slice(0, state.noOfMessages), | |
}; | |
case AlertActionKind.ADD_ERROR: | |
return { | |
...state, | |
messages: [{ type: "error", ...action.payload }, ...state.messages].slice(0, state.noOfMessages), | |
}; | |
case AlertActionKind.REMOVE_MESSAGE: | |
return { ...state, messages: state.messages.filter((message) => message.id !== action.payload.id) }; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/AlertContextProvider.tsx | |
export const AlertContextProvider: FC<{ | |
children: ReactNode; | |
noOfMessages?: number; | |
autoHideTimeout?: number; | |
autoHideError?: boolean; | |
}> = ({ children, noOfMessages = 5, autoHideTimeout = 3000, autoHideError = false }) => { | |
const [state, dispatch] = useReducer(AlertReducer, { messages: [], noOfMessages }); | |
const addSuccessMessage = useCallback( | |
(data: any) => { | |
const id = Date.now(); | |
dispatch({ type: AlertActionKind.ADD_SUCCESS, payload: { id, ...data } }); | |
setTimeout(() => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, autoHideTimeout); | |
}, | |
[dispatch, autoHideTimeout] | |
); | |
const addErrorMessage = useCallback( | |
(data: any) => { | |
const id = Date.now(); | |
dispatch({ type: AlertActionKind.ADD_ERROR, payload: { id, ...data } }); | |
if (autoHideError) { | |
setTimeout(() => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, autoHideTimeout); | |
} | |
}, | |
[dispatch, autoHideError, autoHideTimeout] | |
); | |
const removeMessage = useCallback( | |
(id: any) => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, | |
[dispatch] | |
); | |
return ( | |
<AlertMessengerContext.Provider value={{ state, addSuccessMessage, addErrorMessage, removeMessage }}> | |
{children} | |
<AlertsContainer> | |
{state.messages.map(({ type, title, message, id }) => ( | |
<Alert | |
key={`alert__message__${id}`} | |
type={type} | |
title={title} | |
message={message} | |
gutterTop | |
actionIcon={type === "error"} | |
onAction={type === "error" ? () => removeMessage(id) : undefined}/> | |
))} | |
</AlertsContainer> | |
</AlertMessengerContext.Provider> | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function App() { | |
const { addSuccessMessage, addErrorMessage } = useContext(AlertMessengerContext); | |
const onSuccess = () => { | |
const time = new Date().toLocaleString(); | |
addSuccessMessage({ title: `Success: ${time}`, message: `This message generated at ${time}` }); | |
}; | |
const onError = () => { | |
const time = new Date().toLocaleString(); | |
addErrorMessage({ title: `Error: ${time}`, message: `This message generated at ${time}` }); | |
}; | |
return ( | |
<AppContainer> | |
<Button onClick={onSuccess} variant="success"> | |
Alert Success | |
</Button> | |
<Button onClick={onError} variant="error"> | |
Alert Error | |
</Button> | |
</AppContainer> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/index.tsx | |
import { MouseEvent } from "react"; | |
import { AlertProps, MessageType } from "./interfaces"; | |
import { ActionIconContainer, AlertContainer } from "./styled.components"; | |
const noop = (_?: MouseEvent<HTMLButtonElement>) => {}; | |
const Alert = ({ | |
type = MessageType.SUCCESS, | |
title, | |
message, | |
gutterBottom, | |
gutterTop, | |
actionIcon, | |
onAction, | |
}: AlertProps) => { | |
const ActionIcon = | |
typeof actionIcon === "boolean" && actionIcon ? <button onClick={onAction ?? noop}>X</button> : actionIcon; | |
return ( | |
<AlertContainer type={type} gutterBottom={gutterBottom} gutterTop={gutterTop}> | |
{actionIcon && <ActionIconContainer>{ActionIcon}</ActionIconContainer>} | |
<h4>{title}</h4> | |
<p>{message}</p> | |
</AlertContainer> | |
); | |
}; | |
export default Alert; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/AlertContextProvider.tsx | |
import { createContext, FC, ReactNode, useCallback, useReducer } from "react"; | |
import Alert from "."; | |
import { AlertAction, AlertActionKind, AlertContext, AlertState } from "./interfaces"; | |
import { AlertsContainer } from "./styled.components"; | |
export const AlertMessengerContext = createContext<AlertContext>({} as AlertContext); | |
const AlertReducer = (state: AlertState, action: AlertAction) => { | |
switch (action.type) { | |
case AlertActionKind.ADD_SUCCESS: | |
return { | |
...state, | |
messages: [{ type: "success", ...action.payload }, ...state.messages].slice(0, state.noOfMessages), | |
}; | |
case AlertActionKind.ADD_ERROR: | |
return { | |
...state, | |
messages: [{ type: "error", ...action.payload }, ...state.messages].slice(0, state.noOfMessages), | |
}; | |
case AlertActionKind.REMOVE_MESSAGE: | |
return { ...state, messages: state.messages.filter((message) => message.id !== action.payload.id) }; | |
} | |
}; | |
export const AlertContextProvider: FC<{ | |
children: ReactNode; | |
noOfMessages?: number; | |
autoHideTimeout?: number; | |
autoHideError?: boolean; | |
}> = ({ children, noOfMessages = 5, autoHideTimeout = 3000, autoHideError = false }) => { | |
const [state, dispatch] = useReducer(AlertReducer, { messages: [], noOfMessages }); | |
const addSuccessMessage = useCallback( | |
(data: any) => { | |
const id = Date.now(); | |
dispatch({ type: AlertActionKind.ADD_SUCCESS, payload: { id, ...data } }); | |
setTimeout(() => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, autoHideTimeout); | |
}, | |
[dispatch, autoHideTimeout] | |
); | |
const addErrorMessage = useCallback( | |
(data: any) => { | |
const id = Date.now(); | |
dispatch({ type: AlertActionKind.ADD_ERROR, payload: { id, ...data } }); | |
if (autoHideError) { | |
setTimeout(() => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, autoHideTimeout); | |
} | |
}, | |
[dispatch, autoHideError, autoHideTimeout] | |
); | |
const removeMessage = useCallback( | |
(id: any) => { | |
dispatch({ type: AlertActionKind.REMOVE_MESSAGE, payload: { id } }); | |
}, | |
[dispatch] | |
); | |
return ( | |
<AlertMessengerContext.Provider value={{ state, addSuccessMessage, addErrorMessage, removeMessage }}> | |
{children} | |
<AlertsContainer> | |
{state.messages.map(({ type, title, message, id }) => ( | |
<Alert | |
key={`alert__message__${id}`} | |
type={type} | |
title={title} | |
message={message} | |
gutterTop | |
actionIcon={type === "error"} | |
onAction={type === "error" ? () => removeMessage(id) : undefined} | |
/> | |
))} | |
</AlertsContainer> | |
</AlertMessengerContext.Provider> | |
); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/App.tsx | |
import { useContext } from "react"; | |
import { AlertMessengerContext } from "./components/Alert/AlertContextProvider"; | |
import { AppContainer, Button } from "./components/Alert/styled.components"; | |
function App() { | |
const { addSuccessMessage, addErrorMessage } = useContext(AlertMessengerContext); | |
const onSuccess = () => { | |
const time = new Date().toLocaleString(); | |
addSuccessMessage({ title: `Success: ${time}`, message: `This message generated at ${time}` }); | |
}; | |
const onError = () => { | |
const time = new Date().toLocaleString(); | |
addErrorMessage({ title: `Error: ${time}`, message: `This message generated at ${time}` }); | |
}; | |
return ( | |
<AppContainer> | |
<Button onClick={onSuccess} variant="success"> | |
Alert Success | |
</Button> | |
<Button onClick={onError} variant="error"> | |
Alert Error | |
</Button> | |
</AppContainer> | |
); | |
} | |
export default App; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/index.tsx | |
import { StrictMode } from "react"; | |
import ReactDOMClient from "react-dom/client"; | |
import { createGlobalStyle, ThemeProvider } from "styled-components"; | |
import App from "./App"; | |
import { AlertContextProvider } from "./components/Alert/AlertContextProvider"; | |
const Global = createGlobalStyle` | |
p, | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6 { | |
padding: 0; | |
margin: 0%; | |
} | |
`; | |
const rootElement = document.getElementById("root"); | |
const root = ReactDOMClient.createRoot(rootElement as HTMLElement); | |
const theme = { | |
colors: { | |
message_error: "#fdeded", | |
message_success: "#edf7ed", | |
}, | |
}; | |
root.render( | |
<StrictMode> | |
<ThemeProvider theme={theme}> | |
<AlertContextProvider autoHideError> | |
<Global /> | |
<App /> | |
</AlertContextProvider> | |
</ThemeProvider> | |
</StrictMode> | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/interfaces.ts | |
import { MouseEvent, ReactNode } from "react"; | |
export enum MessageType { | |
SUCCESS = "success", | |
ERROR = "error" | |
}; | |
export interface AlertProps { | |
type?: MessageType; | |
title: string; | |
message?: string; | |
gutterBottom?: boolean; | |
gutterTop?: boolean; | |
actionIcon?: ReactNode | boolean; | |
onAction?: (e?: MouseEvent<HTMLButtonElement>) => void; | |
} | |
export interface Message { | |
id: string; | |
type: MessageType; | |
title: string; | |
message?: string; | |
} | |
export interface AlertState { | |
messages: Message[]; | |
noOfMessages: number; | |
} | |
export enum AlertActionKind { | |
ADD_SUCCESS = "ADD_SUCCESS", | |
ADD_ERROR = "ADD_ERROR", | |
REMOVE_MESSAGE = "REMOVE_MESSAGE", | |
} | |
export interface AlertAction { | |
type: AlertActionKind; | |
payload?: any; | |
} | |
export interface AlertContext { | |
state: AlertState; | |
addSuccessMessage: Function; | |
addErrorMessage: Function; | |
removeMessage: Function; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/components/Alert/styled.components.tsx | |
import styled from "styled-components"; | |
import { AlertProps } from "./interfaces"; | |
export const AppContainer = styled.div` | |
display: flex; | |
justify-content: space-evenly; | |
`; | |
export const Button = styled.button<{ variant: string }>` | |
height: 48px; | |
width: 200px; | |
background-color: ${(p) => p.theme.colors[`message_${p.variant}`]}; | |
`; | |
export const AlertsContainer = styled.div` | |
background: #fff; | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
padding: 10px 20px; | |
overflow-y: auto; | |
max-height: calc(100vh - 100px); | |
`; | |
export const AlertContainer = styled.div<Partial<AlertProps>>` | |
padding: 10px 20px; | |
background: ${(p) => p.theme.colors[`message_${p.type}`]}; | |
margin-top: ${(p) => p.gutterTop && "10px"}; | |
margin-bottom: ${(p) => p.gutterBottom && "10px"}; | |
min-width: 300px; | |
height: 80px; | |
border-radius: 4px; | |
position: relative; | |
padding-top: 40px; | |
`; | |
export const ActionIconContainer = styled.div` | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment