React实现简单易用Toast

栏目: 服务器 · 发布时间: 4年前

内容简介:很多项目中需要实现app中常见的提示效果Toast。这个效果看似简单,实现起来也容易,为了方便,将它封装成npm组件,方便后续使用。 这里也是抛砖引玉,可以查看项目地址一起学习Notification是个容器,里面有一个notice数组。 然后render的时候,循环notices生成一段DOM节点,放到自己的div中。 同时,其还提供一个向notices中添加notice的方法(add)和根据key,在notices中删除notice的方法(remove)。 最后关键的地方,定义一个reRwrite方法,
React实现简单易用Toast

很多项目中需要实现app中常见的提示效果Toast。这个效果看似简单,实现起来也容易,为了方便,将它封装成npm组件,方便后续使用。 这里也是抛砖引玉,可以查看项目地址一起学习 react-comment-toast

使用方法

import Toast from "react-common-toast";
Toast.info("xxx");
Toast.info("xxx",duration,onClose);
复制代码

组件拆分

  1. 首先是支持多个提示,不同提示定制化也可以不同。取名为Notice。
  2. Notice外面还有个容器组件,用来装载Notice并且,暴露一些方法给Toast,起名Notification,是一个单例。
  3. 最后就是Toast组件,负责直接生成不同的Notice,或者销毁Notification。但是其实Toast只是个对象,而不是真正意义的组件。

Notification

Notification是个容器,里面有一个notice数组。 然后render的时候,循环notices生成一段DOM节点,放到自己的div中。 同时,其还提供一个向notices中添加notice的方法(add)和根据key,在notices中删除notice的方法(remove)。 最后关键的地方,定义一个reRwrite方法,该方法接受一些参数,动态的向DOM中插入一个div,然后再向这个div中插入Notification,最后返回一个含有几个操作这个Notification的方法的对象。(这就是动态实现插入DOM的关键)

// Notification是Notice父组件,容器
// 是动态插入和删除DOM节点的核心
// 同时也向上暴露给Toast重写改变自己的方法
import React from "react";
import ReactDOM from "react-dom";
import Notice from "./Notice";
import "./toast.css";

// 统计notice总数 防止重复
let noticeNumber = 0;
// 生成唯一的id
const getUuid = () => {
  return "notification-" + new Date().getTime() + "-" + noticeNumber++;
};
export default class Notification extends React.Component {
 

  constructor(props) {
    super(props);
    this.state = {
      notices: [], // 存储当前有的notices
      hasMask: true // 是否显示蒙版
    };
  }
  add(notice) {
    // 添加notice
    // 创造一个不重复的key
    const { notices } = this.state;
    const key = notice.key ? notice.key : (notice.key = getUuid());
    const mask = notice.mask ? notice.mask : false;
    const temp = notices.filter(item => item.key === key).length;

    if (!temp) {
      // 不存在重复的 添加
      notices.push(notice);
      this.setState({
        notices: notices,
        hasMask: mask
      });
    }
  }
  remove(key) {
    // 根据key删除对应
    this.setState(previousState => {
      return {
        notices: previousState.notices.filter(notice => notice.key !== key)
      };
    });
  }
  getNoticeDOM() {
    const _this = this;
    const { notices } = this.state;
    let result = [];

    notices.map(notice => {
      // 每个Notice onClose的时候 删除掉notices中对应key的notice
      const closeCallback = () => {
        _this.remove(notice.key);
        // 如果有用户传入的onClose 执行
        if (notice.onClose) notice.onClose();
      };

      result.push(
        <Notice key={notice.key} {...notice} onClose={closeCallback} />
      );
    });

    return result;
  }
  getMaskDOM() {
    const { notices, hasMask } = this.state;
    // notices为空的时候 不显示蒙版
    // 始终只有一个蒙版
    if (notices.length > 0 && hasMask == true)
      return <div className="tips-mask" />;
  }
  render() {
    const noticesDOM = this.getNoticeDOM();
    //暂时没有配置蒙版
    const maskDOM = this.getMaskDOM();
    return (
      <div>
        {/*{maskDOM}*/}
        {noticesDOM}
      </div>
    );
  }
}

 // Notification增加一个重写方法
  // 该方法方便Notification组件动态添加到页面中和重写
Notification.reWrite = properties => {
  const { ...props } = properties || {};

  let div = document.createElement("div");
  document.body.appendChild(div);

  const notification = ReactDOM.render(<Notification {...props} />, div);

  return {
    notice(noticeProps) {
      notification.add(noticeProps);
    },
    removeNotice(key) {
      notification.remove(key);
    },
    destroy() {
      ReactDOM.unmountComponentAtNode(div);
      document.body.removeChild(div);
    },
    component: notification
  };
};
复制代码

Notice

主要是负责接受一些参数,duration,icon,content等等

// Notice是Toast最底层组件
// 每个toast的小框框其实都是一个Notice
// Notice核心就是组件初始化的时候 生成一个定时器
// 根据输入的时间 加载一个动画 然后执行输入的回调
// Notice的显示和隐藏收到父组件Notification的绝对控制
import React from "react";
import classNames from "classnames";
import { PropTypes } from "prop-types";

export default class Notice extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      shouldClose: false // 是否开启关闭动画
    };
  }
  componentDidMount() {
    if (this.props.duration > 0) {
      this.closeTimer = setTimeout(() => {
        this.close();
      }, this.props.duration - 300); // 减掉消失动画300毫秒
    }
  }
  componentWillUnmount() {
    // 当有意外关闭的时候 清掉定时器
    this.clearCloseTimer();
  }
  clearCloseTimer() {
    if (this.closeTimer) {
      clearTimeout(this.closeTimer);
      this.closeTimer = null;
    }
  }
  close() {
    // 关闭的时候 应该先清掉倒数定时器
    // 然后开启过场动画
    // 等待动画结束 执行回调
    this.clearCloseTimer();
    const _this = this;
    _this.setState({ shouldClose: true });
    this.timer = setTimeout(() => {
      if (this.props.onClose) {
        this.props.onClose();
      }
      clearTimeout(_this.timer);
    }, 300);
  }
  render() {
    const { shouldClose } = this.state;
    return <div className={classNames({ leave: shouldClose })}>
        {this.props.content}
      </div>
  }
}

Notice.propTypes = {
  duration: PropTypes.number, // Notice显示时间
  content: PropTypes.any, // Notice显示的内容
  onClose: PropTypes.func // 显示结束回调
};

Notice.defaultProps = {
  duration: 3000
};
复制代码

Toast

Toast首先就是要利用Notification.reWrite初始化一个newNotification,并且保持这个Notification为单例。 然后封装一个notice方法,动态的改变这个newNotification。 最后封装几个常用notice方法暴露出去。

import React from "react";
import classNames from "classnames";
import Notification from "./Notification";
// Toast组件比较特殊
// 因为<Toast />不会被直接渲染在DOM中
// 而是动态插入页面中
// Toast组件核心就是通过Notification暴露的重写方法 动态改变Notification
let newNotification;

// 获得一个Notification
const getNewNotification = () => {
  // 单例 保持页面始终只有一个Notification
  if (!newNotification) {
    newNotification = Notification.reWrite();
  }

  return newNotification;
};
// notice方法实际上就是集合参数 完成对Notification的改变
const notice = (content, type, duration = 3000, onClose, mask = true) => {
  if (!content) return;

//   content = content.toString();

  let notificationInstance = getNewNotification();

  notificationInstance.notice({
    duration,
    mask: mask,
    content: (
      <div className={classNames(["tips-notice-box"])}>
        <div
          className={classNames([
            "tips-notice-content",
            { info: type === "info" },
            { success: type === "success" },
            { warning: type === "warning" },
            { error: type === "error" }
          ])}
        >
          {content}
        </div>
      </div>
    ),
    onClose: () => {
      if (onClose) onClose();
    }
  });
};

export default {
  show(content, duration, icon, mask, onClose) {
    return notice(content, undefined, icon, duration, onClose, mask);
  },
  info(content, duration, icon, mask, onClose) {
    return notice(content, "info", icon, duration, onClose, mask);
  },
  success(content, duration, icon, mask, onClose) {
    return notice(content, "success", icon, duration, onClose, mask);
  },
  warning(content, duration, icon, mask, onClose) {
    return notice(content, "warning", icon, duration, onClose, mask);
  },
  error(content, duration, icon, mask, onClose) {
    return notice(content, "error", icon, duration, onClose, mask);
  },
  hide() {
    if (newNotification) {
      newNotification.destroy();
      newNotification = null;
    }
  }
};
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

Larry Ullman / Peachpit Press / 2004-02-02 / USD 29.99

So you know HTML, even JavaScript, but the idea of learning an actual programming language like PHP terrifies you? Well, stop quaking and get going with this easy task-based guide! Aimed at beginning ......一起来看看 《PHP for the World Wide Web, Second Edition (Visual QuickStart Gu》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试