[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