Creating a message component for displaying error in ReactJS
Tags: react, typescript, javascript
Published: 2021-04-10 05:31:26

Hi,
Today I would like to share my way of displaying error or custom messages in React.
The idea is to use context and custom hook so we are able to access the component on all pages.

# Custom Message Context Provider
Let's create a context provider first.

```tsx
// CustomMessageProvider.tsx

import { createContext, useState } from 'react';

/*
* show : whether show or hide the message block
* type : what theme is the message block
* msg : message displaying on the message block
* 
* You can add more field if you want
*/
interface Msg {
  show: boolean;
  type: 'error' | 'info' | 'success' | 'warning';
  msg: string;
}

const defaultMsg = {
    show: false,
    type: 'success',
    msg: ''
} as Msg

export const CustomMessageContext = createContext({
    msg: defaultMsg as Msg,
    setMsg: (_: Msg) => {}
})

export const CustomMessageProvider = ({children}) => {
    const [msgState, setMsgState] = useState(defaultMsg)

// CustomMessageContext contains an object 
// and also a method for setState

    const contextValue = {
        msg: msgState,
        setMsg: (val: Msg) => setMsgState(val)
    }
    return (
        <CustomMessageContext.Provider value={contextValue}>
            {children}
        </CustomMessageContext.Provider>
    );
}
```

# Custom Snackbar
After that, create a custom snackbar using material-ui
```tsx
// CustomSnackBar.tsx
const CustomSnackBar = () => {
    const { msg, setMsg } = useMsg();
    return (
        <Snackbar
          open={msg?.show}
          autoHideDuration={3000}
          onClose={() => {
              setMsg({ 
                  show: false,
                  type: msg.type, 
                  msg: '' 
              });
          }}>
          <Alert severity={msg.type}>{msg.msg}</Alert>
        </Snackbar>
    );
};

const Alert = (props) => {
    return (
        <MuiAlert 
            elevation={6} 
            variant="filled" 
            {...props} 
        />
    );
};

export default CustomSnackBar;
```


Then we can add the provider to `App.tsx` so that we can access the context on all pages.

```tsx
// App.tsx
const App = () => {
    <CustomMessageProvider>
        ...
        <CustomSnackBar />
    </CustomMessageProvider>
}
export default App
```

Great! 

# Custom hook

What we are going to do now is to add a custom hook so we don't have to call useContext in all pages

```tsx
// customMessageHook.tsx
const useMsg = () => {
    const { msg, setMsg } = useContext(CustomMessageContext);
    return { msg, setMsg };
}
export default useMsg;
```

# Integration

We can now call useMsg on all pages.
Let say you want to display an error if the fetch API failed on a page.

```tsx
const { setMsg } = useMsg();

...

const fetchApi = async () => {
    try {
        ...
    } catch (err) {
        setMsg({
            show: true, 
            type: 'error',
            msg: 'Oops, error'
        })
    }
}
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r5ioa8449eqjrs4seky3.png)

# Ending
I know there are some more elegant ways to implement this, feel free to leave me a comment and share what you think. 
Have a nice day.