跳至主要内容

[note] notistack 筆記

keywords: snackbar, toast

客製化 Snackbar

notistack 中使用的是 material UI 的 Snackbar 元件,在 notistack 中,只要使用了 content 這個參數,就表示要完全客製化 snackbar 的外觀,因此像是 action 就無法再使用,但仍可以參考原本 notistack 中的用法(notistack 中的 SnackbarItem 元件原始碼),修改成自己的客製化 Snackbar。

STEP 1:使用 SnackProvider

// components/App.tsx
const App: React.FC = () => (
<ThemeProvider theme={theme}>
<SnackbarProvider
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
autoHideDuration={null}
hideIconVariant
preventDuplicate
>
<Router history={browserHistory}>
<Routes />
</Router>
</SnackbarProvider>
</ThemeProvider>
);

STEP 2:定義 CustomSnackbar

// components/Alert.tsx

import Button from '@material-ui/core/Button';
import React from 'react';
import { SnackbarContent } from '@material-ui/core/';
import { makeStyles } from '@material-ui/core/styles';
import { useSnackbar } from 'notistack';

const useStyles = makeStyles((theme) => ({
/* ... */
}));

type DataType = {
foo: string;
bar: string;
};

type SnackbarProps = {
id: number;
message: React.ReactNode;
data: DataType;
};

// STEP 1:使用 React.forwardRef 來建立客製化的 Snackbar
const StyledSnackbarContent = React.forwardRef<HTMLDivElement, SnackbarProps>((props, ref) => {
const { closeSnackbar } = useSnackbar();
const { id, data } = props;
const { foo, bar } = data;

const classes = useStyles();
return (
<div ref={ref}>
<SnackbarContent
classes={{
root: classes.root,
action: classes.action,
message: classes.message,
}}
message={
<div>
{foo}-{bar}
</div>
}
action={
<Button color="secondary" size="small" onClick={() => closeSnackbar(id)}>
關閉
</Button>
}
/>
</div>
);
});

// STEP 2:content 是函式,它可以接收參數 data 並回傳一個函式
// 這個 data 可以定義自己需要傳入 StyledSnackbarContent 的資料
// 後面回傳的 function 則是 notistack 用來接收 key 和 message 的 callback function
export const content = (data: DataType) => (key: number, message: string): React.ReactNode => {
// 把取得的資料傳入 StyledSnackbarContent 中
return <StyledSnackbarContent id={key} message={message} data={data} />;
};

export default content;

STEP 3:呼叫該 snackbar

// Dashboard.tsx
// STEP 1:匯入對應的 module
import Alert from 'components/alert';
import { useSnackbar } from 'notistack';

const Dashboard = () => {
// STEP 2:取得 notistack 提供的 enqueueSnackbar
const { enqueueSnackbar } = useSnackbar();

const handleClick = () => {
// STEP 3:使用 enqueueSnackbar 來觸發 Snackbar
enqueueSnackbar(null, {
// STEP 4:content 的地方帶入客製化樣式,裡面可以帶入客製化資料(data)
content: Alert({
foo: 'foo',
bar: 'bar',
}),
persist: true,
key: data.id, // 預設會自己產生 key,也可以使用自己資料中的 id
});
};
return <button onClick={handleClick}>Alert</button>;
};

其他

錯誤:Cannot read property 'getBoundingClientRect' of null using content

keywords: React.forwardRef

如果搭配使用 notistack 這個套件出現錯誤訊息為 Cannot read property 'getBoundingClientRect' of null using content 時,可能是忘了把取得的 ref 帶入元件中。

也就是在 <div className={...} ref={ref}> 這裏要記得把 ref 傳入:

const PaymentDialog = React.forwardRef((props, ref) => {
const classes = useStyles();

return (
<div className={classes.container} ref={ref}>
// ...
)
}

Cannot read property 'getBoundingClientRect' of null using content @ notistack github issue