小白篇--手写 React 全局提示 `Message` 组件思路

2,868 阅读1分钟

写前:空余时间,看 antd 发现此组件有趣,便模仿写来玩,不喜勿喷,望提建议。

先上效果图:

Message.js

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

import "./style.less";

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

function notice(args) {
  return ReactDOM.render(<Message {...args} />, div);
}

let timer;

function Message(props) {
  const [msgs, setMsgs] = useState([]);
  const { content, type } = props;

  const iconObj = {
    info: "mf-icon-information",
    success: " mf-icon-selected2",
    warn: "mf-icon-Prompt",
    error: " mf-icon-remove",
  };

  useEffect(() => {
    setMsgs([...msgs, props]);
  }, [props]);

  useEffect(() => {
    if (msgs.length) {
      let msgscopy = JSON.parse(JSON.stringify(msgs));

      // setInterval 写法
      clearInterval(timer);
      timer = setInterval(
        (setMsgs) => {
          msgscopy.shift();
          setMsgs(JSON.parse(JSON.stringify(msgscopy)));
          if (!msgscopy.length) {
            clearInterval(timer);
          }
        },
        props.duration * 1000,
        setMsgs
      );

      // clearTimeout 写法
      // clearTimeout(timer);
      // let fn = (setMsgs) => {
      //   msgscopy.shift();
      //   setMsgs(JSON.parse(JSON.stringify(msgscopy)));
      //   if (msgscopy.length) {
      //     timer = setTimeout(fn, props.duration * 1000, setMsgs);
      //   }
      // };
      // timer = setTimeout(fn, props.duration * 1000, setMsgs);
    }
  }, [msgs]);

  return (
    <div className="message">
      {msgs.map((item, index) => {
        return (
          <div className="message-content" key={index}>
            <i className={`${iconObj[type]} message-content-icon` }></i>
            <span className="message-content-text">{content}</span>
          </div>
        );
      })}
    </div>
  );
}

let api = {};

["info", "success", "warn", "error"].forEach((type) => {
  api[type] = (content, duration = 3) => {
    return notice({ content, duration, type });
  };
});

export default api;

// 目前该组件只包含了 "info", "success", "warn", "error" 这几种,其他的按需自行添加即可;
// `duration` 为时长;
// 具体可参照 ant design 完善;

style.less

.message {
  position: fixed;
  top: 85px;
  left: 50%;
  transform: translate(-50%, 0);
  z-index: 1000;

  &-content {
    max-width: 800px;
    background: rgba(0, 0, 0, 0.6);
    padding: 13px 64px;
    border-radius: 6px;
    margin-top: 8px;

    font-size: 14px;
    font-family: Avenir-Medium, Avenir;
    font-weight: 500;
    color: rgba(255, 255, 255, 1);
    line-height: 22px;
    display: flex;
    align-items: flex-start;

    &-icon{
      margin-right: 8px;
      font-size: 20px;
    }
  }
}

  • 用这种形式 ReactDOM.render(<Message {...args} />, div); 来连接 ReactElement 和 DOMElement ;
  • 通过调用 notice() 方法来动态渲染内容;
  • msgs 数组来显示多条和取消显示 message 组件;

Index.js

import React from "react";
import message from "./Message";

function Index (){
	
	const handleClick = ()=>{
		message.warn('我是全局提示组件');
		// message.info('我是全局提示组件'); 其他提示类型都按照此方式即可。
		// message.info('我是全局提示组件',4); 第二个参数为自定义时长。
	}
	
	return (
		<div>
			<button onClick={handleClick}>click me</button>
		</div>
	)
}
export default Index;