React 封装 Echarts 公共组件

2019-09-20 admin

前序

LZ 之前工作一直在用 Vue,但最近听说 Vue 新版也要 All IN JS,所以想着干脆换到 React 算了,所以目前在学习 React + TS + Hook,顺手拿了一个老项目重构,今天主要讲 React 封装 Echarts 公共组件, 因为第一次正式搞,所以本文中如果有 React 代码哪里不规范还请大佬们批评指正哈!

目录:

  1. 需求分析
  2. 技术评估
  3. 实现思路
  4. 测试优化
  5. 总结分享

1. 需求分析

  • ECharts 图表要用到很多,所以要将公共部分抽取成组件减少重复代码
  • ECharts 是需要操作到真实dom的第三方库,和MVVM框架一起使用需要做一些特殊处理
  • Vue项目中使用的Vue-ECharts,组件自动去处理实例挂、 数据更新等
  • React也有echarts-for-react等优秀的开源组件,但为了学习和更舒适的使用,我们需要自己去造新的轮子
  • 参考资料:

2. 技术评估

我们需要用到四个东西:

  • React
  • React Hook
  • Echarts
  • TypeScript

3. 实现思路

1) 一个组件需要的参数配置 ChartProps

参数 描述 必填
key string 用于保持多图表时每一个图表的独立性
option object 或者 null Echarts 配置参数
style { width: string, height: string } 用于保持多图表时每一个图表的独立性
className string 组件样式类 className
onRender onRender?(instance): void; 渲染时回调函数,返回图表示例

2) 参数类型检查接口 interface

interface ChartProps {
    key: string;
    option: object | null;
    style: {
        width: string;
        height: string;
    };
    className?: string;
    onRender?(instance): void;
}

3) 基础组件 Chart.tsx

import * as React from "react";
import echarts from "echarts";

const Chart = (props: ChartProps): React.ReactElement => {
    // 挂载节点
    let chartDom = null;

    // 元素挂载到浏览器事件
    const refOnRender = (el): void => chartDom = el;

    // 返回组件
    return React.createElement("div", {
        ref: refOnRender,
        style: props.style,
        className: props.className
    });

};

export default Chart;

4) 当组件挂载到真实DOM,初始化Echarts实例,使用Hook

// 生命钩子函数
type Callback = () => void;
React.useEffect((): Callback => {

    // 加载状态
    function showLoading(instance): void {
        instance.showLoading("default", {
            text: "",
            color: "#c23531",
            textColor: "#000000",
            maskColor: "rgba(255, 255, 255, 0.8)",
            zlevel: 0
        });
    }

    // 获取实例对象
    let instance = echarts.getInstanceByDom(chartDom) || echarts.init(chartDom);

    // 默认加载状态
    showLoading(instance);

    // 如果存在参数,渲染图表
    if (props.option) {
        // 关闭加载状态
        if (instance) instance.hideLoading();
        // 渲染图表
        instance.setOption(props.option);
    }

}, [props.option]);

5)浏览器窗口大小变化时图表大小自适应重绘

// 大小自适应
const resize = (): void => instance.resize();
window.removeEventListener("resize", resize);
window.addEventListener("resize", resize);
  1. 给图表加动画需要图表实例,设置回调函数将图表实例返回
// 回调函数返回实例
if (props.onRender) props.onRender(instance);

7) 组件销毁时清除 监听器组件状态值

// 销毁并清除状态
return (): void => {
    echarts.dispose(instance);
    window.removeEventListener("resize", resize);
};

8) 最终完整组件

import * as React from "react";
import echarts from "echarts";

/**
 * 参数列表
 * key: string; 唯一值
 * option: object | null; 图表数据
 * style: {
 *      width: string; 图表宽度
 *      height: string; 图表高度
 * };
 * className?: string; 图表CSS样式类名称
 * onRender?(instance): void; 图表回调函数返回图表实例
 */

interface ChartProps {
    key: string;
    option: object | null;
    style: {
        width: string;
        height: string;
    };
    className?: string;

    onRender?(instance): void;
}

const Chart = (props: ChartProps): React.ReactElement => {

    // 挂载节点
    let chartDom = null;

    // 生命钩子函数
    type Callback = () => void;
    React.useEffect((): Callback => {
        console.log("useEffect");

        // 加载状态
        function showLoading(instance): void {
            instance.showLoading("default", {
                text: "",
                color: "#c23531",
                textColor: "#000000",
                maskColor: "rgba(255, 255, 255, 0.8)",
                zlevel: 0
            });
        }

        // 获取实例对象
        let instance = echarts.getInstanceByDom(chartDom) || echarts.init(chartDom);

        // 大小自适应
        const resize = (): void => instance.resize();
        window.removeEventListener("resize", resize);
        window.addEventListener("resize", resize);

        // 默认加载状态
        showLoading(instance);

        // 渲染图表
        if (props.option) {
            if (instance) instance.hideLoading();
            instance.setOption(props.option);
        }

        // 回调函数返回实例
        if (props.onRender) props.onRender(instance);

        // 销毁并清除状态
        return (): void => {
            echarts.dispose(instance);
            window.removeEventListener("resize", resize);
        };

    }, [chartDom, props]);

    // 元素挂载到浏览器事件
    const refOnRender = (el): void => chartDom = el;

    // 返回组件
    return React.createElement("div", {
        ref: refOnRender,
        style: props.style,
        className: props.className
    });

};

// 导出组件模块
export default Chart;

测试优化

你可以:

  1. 自行搭建React+TS+Echarts开发环境
  2. 使用 Clone 本项目测试(暂不支持 sry)

主要代码 Test.tsx

import * as React from "react";
// 导入 Chart 组件
import Chart from "../chart";

const chartEmpty = {
    title: {
        text: "暂无数据",
        show: true,
        textStyle: {
            color: "grey",
            fontSize: 20
        },
        left: "center",
        top: "center"
    }
};

const Test= (): React.ReactElement => {
    // 图表1数据
    let [chart1Data, setChart1Data] = React.useState(null);
    // 图表2数据
    let [chart2Data, setChart1Data] = React.useState(null);

    // 防护监控数据 实例
    let [chart1, chart2] = [null, null];

    // 模拟异步更新图表数据
    function updateChart(): void{
        let opts: null;

        chartEmpty.title.text = "图表 1 暂无数据" + (+new Date());
        opts = JSON.parse(JSON.stringify(chartEmpty));
        setChart1Data(opts);

        chartEmpty.title.text = "图表 2 暂无数据" + (+new Date());
        opts = JSON.parse(JSON.stringify(chartEmpty));
        setChart2Data(opts);
    }

    // 获取图表实例,添加自定义图表事件
    React.useEffect((): void => {
        console.log("chart1", chart1);
    }, [chart1]);

    // 返回组件
    return (
        <div>
           <Chart
                key="chart1"
                className="chart1"
                option={chart1Data}
                onRender={(e): void => chart1 = e}
                style={{width: "100%", height: "400px"}}/>
           <hr>       
           <Chart
                key="chart2"
                className="chart2"
                option={chart2Data}
                style={{width: "100%", height: "400px"}}/>
           <button onClick={updateChart}> 异步更新图表数据 </button>
        </div>
    )
}

export default Test;

总结分享

学习了React组件的封装,由此及彼,以后其他第三方库应该也可以轻松集成了 学习了React Hook使用方法。 学习了React + TS的开发模式。 不到100行代码的组件相比于echarts-for-react等其他优秀组件的少了很多高级和细节自定义。(开源的话会加进去吧) 欢迎大佬检阅。

[转载]原文链接:https://segmentfault.com/a/1190000020445644

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-75616.html

文章标题:React 封装 Echarts 公共组件

相关文章
详解angular2封装material2对话框组件
1. 说明 angular-material2自身文档不详,控件不齐,使用上造成了很大的障碍。这里提供一个方案用于封装我们最常用的alert和confirm组件。 2. 官方使用方法之alert ①编写alert内容组件 @Componen...
2017-03-13
在 mpvue 使用 echarts 小程序组件
具体操作 下载 echarts-for-weixin 。 把其 ec-canvas 目录移动到 mpvue 项目的 static 目录下。 对 ec-canvas/ec-canvas.js 进行小调整,考虑提 pr 到 ec-c...
2018-03-11
Vue 短信验证码组件开发详解
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 Vue.js 自身不是一个全能框架——它只聚焦于视图层。因此...
2017-03-17
Element el-upload上传组件详解
upload上传是前端开发很常用的一个功能,在Vue开发中常用的Element组件库也提供了非常好用的upload组件。 基本用法 代码: &lt;el-upload :action=&quot;uploadActionUrl&quot;&...
2018-03-18
使用iview的组件 Table 表格,有固定列,设置其中一个列适应屏幕大小
描述 在使用iview的组件Table表格时,有固定列,表格列宽不等。 在表格平铺时,不能自适应宽度。 问题 每个列有需要设置的宽度,有固定的列,很难调整某一列的宽度为刚刚好的。此时需要某一列自适应宽度。 解决 &lt;template...
2018-09-28
有赞vant-ui Tabs、List、PullRefresh组件实践
功能需求是实现一个移动端的栏目列表切换,于此同时列表需要进行下拉刷新,上拉加载 如下图,大概是一个这样的东西 看起来是挺简单的,实现起来很方便。没错,我当时也是这么想的,结果满心欢喜就开始在前一个前端写好的的vantui 基本结构下开始编...
2018-08-21
vuejs2.0实现分页组件使用$emit进行事件监听数据传递的方法
如果我有几个页面都需要有分页效果,不可能每个页面都去复制一下这段代码吧,意思是封装一下,变成通用的组件。 首先使用基础 Vue 构造器,创建一个“子类”,Vue.extend( options ) var barHtml = &#x27;&...
2017-03-15
js提取中文拼音首字母的封装工具类
前言 本文主要记录了如何用js提前中文拼音首字母的方法。封装一个函数,假如有需要的,可以直接拿去用。下面话不多说了,来一起看看详细的介绍吧。 原理 主要是根据中文的unicode码来进行的。主要是在收集的中文范围内查找,大家可以多收集一些。...
2018-03-13
深入理解JavaScript的React框架的原理
如果你在两个月前问我对React的看法,我很可能这样说: 我的模板在哪里?javascript中的HTML在做些什么疯狂的事情?JSX开起来非常奇怪!快向它开火,消灭它吧! 那是因为我没有理解它. 我发誓,React 无疑是在正确的轨道...
2017-03-26
angular2倒计时组件使用详解
项目中遇到倒计时需求,考虑到以后在其他模块也会用到,就自己封装了一个组件。便于以后复用。 组件需求如下: 接收父级组件传递截止日期 接收父级组件传递标题 组件效果 变量 组件countdown.html代码 &lt;div clas...
2017-03-11
回到顶部