博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Material UI框架下SnackBar(消息条)的高级用例--notistack
阅读量:798 次
发布时间:2019-03-25

本文共 7885 字,大约阅读时间需要 26 分钟。

        notistack是React下Material UI框架的SnackBar(消息条)的高级用例,该用例能同时显示多个独立消息条,使用起来也非常简单。在全局初始化之后就可以在网页上任何需要的地方使用它,而不用为每个页面导入一个Material UI原生的消息条。

       该用例是基于Material UI的SnackBar,做了一下功能和界面封装。下面我们从头来开始学习如何使用它。

一、新建React工程

       我们的第一步照样是新建React工程,在工作目录下执行:

npx create-react-app notistackdemo

       耐心等待安装完毕。因为notistack是基于Matrial UI,所以需要再安装相应的库:

cd notistackdemonpm install @material-ui/core --savenpm install @material-ui/icons --savenpm install notistack --save

       好了,我们的准备工作完成了。

二、简单使用notistack

       notistack提供了一个provider,在使用之前必须先初始化这个provider,首先我们将src/index.js稍微增加一点内容改写成这样:

import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import * as serviceWorker from './serviceWorker';import {
SnackbarProvider } from 'notistack'ReactDOM.render(
, document.getElementById('root'));// If you want your app to work offline and load faster, you can change// unregister() to register() below. Note this comes with some pitfalls.// Learn more about service workers: https://bit.ly/CRA-PWAserviceWorker.unregister();

       接下来,我们在主页面上增加一个按钮,用来点击显示消息条。修改src/App.js,在导入语句中增加下面两行:

import Button from '@material-ui/core/Button';import {
useSnackbar } from 'notistack';

       然后在函数组件App里使用如下hook:

const {
enqueueSnackbar } = useSnackbar()

       最后再增加一个按钮和对应的处理程序,最后修改完成的App.js完整代码如下:

import React from 'react';import logo from './logo.svg';import './App.css';import Button from '@material-ui/core/Button';import {
useSnackbar } from 'notistack';function App() {
const {
enqueueSnackbar } = useSnackbar(); const handleClick = event => {
event.preventDefault(); enqueueSnackbar("This is a message",{
variant:"success"}); }; return (
logo

Edit src/App.js and save to reload.

Learn React
);}export default App;

       运行npm start,在主页面上点击按钮,就可以看到左下角会显示一个绿色的消息条了。好了,最基本的使用我们已经学习完成了,接下来我们学习一些稍微高级点的用法。

三、notistack高级用法

       notistack的文档地址为:

       从它的文档中我们看到可以自己设置很多属性,比如消息条的显示时间,关闭消息条后的回调函数等等。因为把全部属性在src/index.js中设置并不是很整洁,于是我们新建一个NotistackWrapper来进行集中设置。我们先ctrl + c来关闭开发服务器的运行。

       在src/目录下新建NotistackWrapper.js,里面代码如下:

//本JS进行一些notistack的常用设置import React from 'react';import {
SnackbarProvider } from 'notistack';import {
isMobile } from 'react-device-detect'/*** 显示的消息条的最大数量,如果超过,会关掉最先打开的然后再显示新的,是一个队列* 如果只想显示1个,设置为1,3是默认值*/const MAX_SNACKBAR = 3//设置自动隐藏时间,默认值是5秒,也就是5000毫秒const AUTO_HIDE_DURATION = 3000//设置消息条位置,默认值为底部左边const POSITION = {
vertical: 'bottom', horizontal: 'left'}export default function NotistackWrapper({
children}) {
return (
{
children}
)}

       然后再改写src/index.js,使用刚才自定义包装器来代替SnackbarProvider,将第6行到第13行代码改为:

import NotistackWrapper from './NotistackWrapper.js'ReactDOM.render(    
, document.getElementById('root'));

       这里我们做了移动端适配,移动端显示时让消息条底部靠边,用react-device-detect库来判断是否移动端,需要进行安装。

npm install  react-device-detect --save

       让我们再次运行npm start,大家可以自己对照一下文档更改一下参数,来查看一下效果。

四、增加回调函数

       有时,我们需要在消息条关闭时进行一些操作,比如提示完成之后的页面跳转等,这时就需要增加一个回调函数。

       让我们改写一下src/App.jshandleClick的定义,在显示一个消息条时增加onClose属性。

const handleClick = event => {
event.preventDefault(); // variant could be success, error, warning, info, or default let options = {
variant:"success", onClose:() => console.log("close a snackbar") }; enqueueSnackbar("This is a message", options); };

       注意variant属性只能是上面列举的几种值。

       打开Chrome浏览器的开发者工具,在console那一栏就能看到我们打印的log。

在这里插入图片描述
至此,一个简单的关闭时回调函数示例就完成了。

五、修复一个问题

       注意

       notistack这里有一个问题,使用上面的例子很容易看到,当我们点击按钮显示一个消息条时,点击屏幕任何位置,可以看到我们的log会输出close a snackbar,也就是我们的onClose回调函数会被触发一次。然后等消息条结束时,会又触发一次,再次打印出log,这才是onClose回调函数需要真正执行的时候。我们在开发者工具里点击右键,清理console,然后在页面上点击按钮,就能清楚的复现我刚才提到的问题。

回调执行了两次
       从图中可以看到回调执行了两次。

       这个问题,也许是我没有仔细查看notistack的文档导致用法不对,也许是它本身的问题。我已经向作者发了邮件进行请教,在未得到作者的回应之前,让我们先采取一个临时措施来处理它。

打开node_modules\notistack\build\SnackbarItem目录下的SnackbarItem.js,找到第77行,也就是_this.handleClose这个函数。修改它的定义,在第79行var snack = _this.props.snack;上面增加以下代码片断:

if(reason === _constants.REASONS.CLICKAWAY) {
return;}

       保存之后我们ctrl + c,然后再次运行npm start。这时可以看到,消息条出现时无论你点击屏幕多少下,都没有log输出了。

       如果是我操作上存在的问题,请大家指正后我再更新文章。

       补充

       这个问题我和作者进行了几次邮件沟通,作者回信中指出这不是一个问题或者bug,而就是这样设计的,点击网页上任意地方(‘clickaway’)都会触发回调执行,因为有些人可能需要在此做一些处理。回调函数有两个参数event和reason,可以使用第二个参数来做判断和过滤。虽然我觉得任意点击执行关闭回调怪怪的,可以增加一个任意点击回调函数嘛,但是问题还是得到了解决。将上面的回调函数改写如下:

const handleClick = event => {
event.preventDefault(); // variant could be success, error, warning, info, or default let options = {
variant:"success", onClose:(_blank,reason) => {
if(reason === 'clickaway') {
return; } console.log("close a snackbar") } }; enqueueSnackbar("This is a message", options);};

这样,再次点击屏幕任意位置,代表回调执行的log就不再输出了。感谢作者的耐心回复。

六、为消息条增加关闭按钮

       有时用户可能不想等待消息条显示特定时间(比如3秒)而是想读完之后立刻关掉它。这时我们可以在消息条上增加一个关闭按钮。首先让我们修改src/NotistackWrapper.js,在导入语句中增加关闭按钮导入:

import IconButton from '@material-ui/core/IconButton';import CloseIcon from '@material-ui/icons/Close';

       然后我们再增加点击关闭功能,修改完成的最终代码如下:

//本JS进行一些notistack的常用设置import React from 'react';import {
SnackbarProvider } from 'notistack';import {
isMobile } from 'react-device-detect';import IconButton from '@material-ui/core/IconButton';import CloseIcon from '@material-ui/icons/Close';/*** 显示的消息条的最大数量,如果超过,会关掉最先打开的然后再显示新的,是一个队列* 如果只想显示1个,设置为1,3是默认值*/const MAX_SNACKBAR = 3//设置自动隐藏时间,默认值是5秒const AUTO_HIDE_DURATION = 10000//设置消息条位置,默认值为底部左边const POSITION = {
vertical: 'bottom', horizontal: 'left'}export default function NotistackWrapper({
children}) {
const notistackRef = React.createRef(); const onClickDismiss = key => () => {
notistackRef.current.closeSnackbar(key); } return (
(
)} > {
children}
)}

       保存后页面会自动刷新,此时再点击按钮,出现的消息条就有一个关闭按钮了,点击这个按钮可以关掉它。

       再次注意

       等等,有问题?如果读者边读边操作可能会发现,手动点击关闭按钮关掉消息条时,没有打印log,也就是没有触发onClose回调。这里我看了一下源码,点击这个按钮时并不是通过具体的SnackbarItem本身来处理的,而是在它的上一层处理,所以就没有执行回调。这个问题我同样发邮件向作者请教了,也可能是我用法的问题。不过目前我们仍然采取一个临时措施来处理它。

       我们需要修改node_modules\notistack\build\SnackbarProvider.js这个文件,找到_this.handleCloseSnack这个方法定义,在第202行左右,将它里面的_this.setState那一段代码加上一点内容(大概217行下面),整个代码片断如下:

_this.setState(function (_ref3) {
var snacks = _ref3.snacks, queue = _ref3.queue; return {
snacks: snacks.map(function (item) {
if (!shouldCloseAll && item.key !== key) {
return _extends({
}, item); } if(item.key === key) {
if(reason === null) {
if(item.onClose) {
item.onClose() } } } return item.entered ? _extends({
}, item, {
open: false }) : _extends({
}, item, {
requestClose: true }); }), queue: queue.filter(function (item) {
return item.key !== key; }) // eslint-disable-line react/no-unused-state };});

       然后保存,ctrl + c 之后再npm start,这时你再点击消息条上的关闭按钮,就会有正确的log输出了。好了,我们来看完工之后的页面。

在这里插入图片描述

       补充

       原作者回信之后确认这是一个bug或者不完善的地方,承诺在下一版本修复它。当前notistack已经更新了,这个问题已经修复,不存在了。特此声明,感谢原作者。

七、总结

       我本来也写了一个类似notistack这样的一次初始化全局使用的消息条组件(很简单,并且只能显示一个消息条)并准备写在CSDN上。但是当我在Material UI 文档中看到了notistack的用法后我便立即决定抛弃我自己所写的组件而向大家推荐它(已经有更好的轮子了,何必自己再弄一个)。它和Material UI原生的消息条相比最大的优点是封装了多消息条队列,相互之间独立不影响。

       本来最初只打算写一个notistack的最简单应用,后来想想已经写了就多加一点点功能吧,没想到增加的功能使用起来会有那么一点点小问题(目前是关闭时的回调函数调用)。这些问题也是我在写文章的过程中遇到的,因为我之前也没有用过,属于现学现写。示例很简单,虽然主体文件只有三个简单的js(不含修改的库文件),却也花了不少时间(包含自己摸索问题的临时解决方法)。看来,在CSDN上写一篇原创文章也是不容易的。

       本文中涉及到关闭回调(onClose)时出现了两个问题,虽然我都临时处理了,但还是需要原作者回应后再找到相应的解决办法才行,这样风险才最小。所以建议使用notistack做消息条的读者先暂时不要使用回调功能。期望有的读者能深入研究一下,找到更好的解决办法。

       本文中介绍的消息条功能也可以和我的一篇《从零开始构建React下的多语言实现》中介绍的多语言功能集成在一起,也就是在一个工程中将这两者都应用。有兴趣的读者可以试一下。

       修改后的gitee上的代码为:

       欢迎留言指出错误或者提出宝贵改进意见。

转载地址:http://ltpyk.baihongyu.com/

你可能感兴趣的文章