├── .gitignore
├── README.md
├── config-overrides.js
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.js
├── App.test.js
├── components
│ ├── ChartSettingBoard.jsx
│ ├── ConfigDropBox.jsx
│ ├── DragElement.jsx
│ ├── DropConfigChart.jsx
│ ├── DropElement.jsx
│ ├── chartSettingBoard.css
│ ├── configDropBox.css
│ ├── dragElement.css
│ ├── dropElement.css
│ └── echartConfig.js
├── img
│ └── view.gif
├── index.css
├── index.js
└── registerServiceWorker.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.history
6 |
7 |
8 | # misc
9 | .DS_Store
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 | .mp4
15 |
16 | npm-debug.log*
17 | yarn-debug.log*
18 | yarn-error.log*
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React-Quick-eChart
2 | react快速配置echart图形
3 |
4 | 写了一个简单的可实现通过配置文件,对不同数据形态直接拖拽生成echart图表;
5 |
6 | 
7 |
8 | # 说明
9 |
10 | 引用的插件
11 | 1、react-dnd 拖拽
12 | 2、react-color 颜色选择
13 | 3、ant 布局框架(非必须)
14 | 4、echart 图标插件
15 |
16 | # 运行
17 | npm install 安装
18 | npm start 运行本地环境
19 | ps:组件没有做封装,代码有些乱,仅供参考
20 |
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | const { injectBabelPlugin } = require('react-app-rewired');
2 |
3 | module.exports = function override(config, env) {
4 | // do stuff with the webpack config...
5 | config = injectBabelPlugin(
6 | ['import', { libraryName: 'antd', libraryDirectory: 'es', style: 'css' }],
7 | config,
8 | );
9 | return config;
10 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "antd-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "antd": "^3.9.1",
7 | "babel-plugin-import": "^1.8.0",
8 | "echarts": "^4.1.0",
9 | "echarts-for-react": "^2.0.14",
10 | "immutability-helper": "^2.7.1",
11 | "react": "^16.4.2",
12 | "react-app-rewired": "^1.6.2",
13 | "react-color": "^2.14.1",
14 | "react-dnd": "^5.0.0",
15 | "react-dnd-html5-backend": "^5.0.1",
16 | "react-dom": "^16.4.2",
17 | "react-scripts": "1.1.5"
18 | },
19 | "scripts": {
20 | "start": "react-app-rewired start",
21 | "build": "react-app-rewired build",
22 | "test": "react-app-rewired test --env=jsdom",
23 | "eject": "react-scripts eject"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingxiaoyiyio/React-Quick-eChart/00685043651498750e95583da3e11364bd8134cb/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component} from 'react';
2 | import ChartSettingBoard from './components/ChartSettingBoard'
3 |
4 | class App extends Component {
5 | render() {
6 | return (
7 |
8 |
9 | );
10 | }
11 | }
12 |
13 | export default App;
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/src/components/ChartSettingBoard.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Row, Col, Select, Icon } from 'antd'
3 | import { DragDropContext } from 'react-dnd';
4 | import HTML5Backend from 'react-dnd-html5-backend';
5 | import DropConfigChart from './DropConfigChart';
6 | import DropElement from './DropElement';
7 | import update from 'immutability-helper';
8 | import echartConfig from './echartConfig';
9 | import './chartSettingBoard.css'
10 |
11 | import ConfigDropBox from './ConfigDropBox'
12 | import DragElement from './DragElement'
13 |
14 | const Option = Select.Option;
15 | const dragItem = 'item';
16 | const colorSet = ['#9CC5B0', '#C9856B', '#6F9FA7', '#334553', '#B34038', '#7D9D85', '#C1883A']
17 |
18 | const lineData = [
19 | { name: '年销量', type: 'string', value: 'year', id: 0, data: ['2013', '2014', '2015', '2016', '2017', '2018'], color: '#9CC5B0', chart: 'line' },
20 | { name: '华北', type: 'value', value: 'h', id: 1, data: [40, 80, 20, 120, 140, 50], color: '#C9856B', chart: 'line' },
21 | { name: '华东', type: 'value', value: 'd', id: 2, data: [140, 180, 120, 40, 50, 150], color: '#6F9FA7', chart: 'line' },
22 | { name: '华南', type: 'value', value: 'n', id: 3, data: [110, 143, 68, 90, 120, 130], color: '#334553', chart: 'line' }
23 | ];
24 | const pieData = [
25 | { value: 335, name: '京东', type: 'value', id: 0, color: '#9CC5B0' },
26 | { value: 310, name: '菜鸟', type: 'value', id: 1, color: '#C9856B' },
27 | { value: 234, name: '总部', type: 'value', id: 2, color: '#6F9FA7' },
28 | { value: 135, name: '小电商', type: 'value', id: 3, color: '#334553' },
29 | { value: 1548, name: '大电商', type: 'value', id: 4, color: '#B34038' }
30 | ]
31 | const chartType = [
32 | { value: 'line', name: '折线图' },
33 | { value: 'bar', name: '柱状图' },
34 | { value: 'pie', name: '饼图' }
35 | ]
36 |
37 | export class ChartSettingBoard extends Component {
38 |
39 | constructor(props) {
40 | super(props);
41 | this.dragEleMove = this.dragEleMove.bind(this);
42 | this.beginDrag = this.beginDrag.bind(this)
43 | this.canDrop = this.canDrop.bind(this)
44 | this.endDrag = this.endDrag.bind(this)
45 | this.delItem = this.delItem.bind(this)
46 | this.changeItem = this.changeItem.bind(this)
47 | this.onSelectChartType = this.onSelectChartType.bind(this)
48 | }
49 |
50 | state = {
51 | activeId: '',
52 | activeDropId: '',
53 | itemList: lineData,
54 | chartType: 'line',
55 | dropConfig: echartConfig['line']
56 | }
57 |
58 | dragEleMove(id) {
59 | this.setState({ activeDropId: id })
60 | }
61 |
62 | beginDrag(id) {
63 | this.setState({ activeId: id })
64 | }
65 |
66 | canDrop(id) {
67 | const { itemList, activeId, dropConfig } = this.state;
68 | if (itemList[activeId].type != dropConfig[id].type) {
69 | return false;
70 | }
71 | return true
72 | }
73 |
74 | endDrag() {
75 | const { itemList, activeId, dropConfig, activeDropId } = this.state;
76 | const ilist = update(itemList, { $splice: [[activeId, 1]] })
77 | const dlist = update(dropConfig, { [activeDropId]: { items: { $push: [itemList[activeId]] } } })
78 | this.setState({ itemList: ilist, dropConfig: dlist })
79 | }
80 |
81 | delItem(item, pitem, pid) {
82 | const { itemList, dropConfig } = this.state;
83 | for (let i = 0; i < pitem.items.length; i++) {
84 | if (pitem.items[i].id === item.id) {
85 | pitem.items.splice(i, 1);
86 | break;
87 | }
88 | }
89 | const nlist = update(itemList, { $push: [item] })
90 | const dropList = update(dropConfig, { [pid]: { $set: pitem } })
91 | this.setState({ itemList: nlist, dropConfig: dropList })
92 | }
93 |
94 | onSelectChartType(type) {
95 | if (type === 'pie') {
96 | this.setState({ itemList: pieData, dropConfig: echartConfig[type], chartType: type })
97 | } else {
98 | const nlist = [...lineData];
99 | for (let i = 0; i < nlist.length; i++) {
100 | nlist[i].chart = type;
101 | }
102 | this.setState({ itemList: nlist, dropConfig: echartConfig[type], chartType: type })
103 | }
104 | }
105 |
106 | changeItem(value, key, id, pid) {
107 | const { dropConfig } = this.state;
108 | const nitem = { ...dropConfig[pid].items[id] }
109 | nitem[key] = value;
110 | const dropList = update(dropConfig, { [pid]: { items: { [id]: { $set: nitem } } } })
111 | this.setState({ dropConfig: dropList })
112 | }
113 |
114 | render() {
115 | const { itemList, dropConfig } = this.state
116 | const leftItems = itemList.map((item, idx) => {
117 | return (
118 |
119 |
120 |
121 | )
122 | })
123 |
124 | const dropList = dropConfig.map((item, idx) => {
125 | const items = item.items.map((sitem, sid) => {
126 | return (
127 |
128 | this.changeItem(value, key, sid, idx)} />
129 |
130 | )
131 | })
132 |
133 | return (
134 | 3 ? 'shortBox' : 'longBox'}>
135 |
136 | {items}
137 |
138 |
139 | )
140 | })
141 | return (
142 |
143 |
可视化
144 |
145 |
146 |
147 |
165 |
166 |
167 | {leftItems}
168 |
169 |
170 |
171 |
172 | {dropList}
173 |
174 |
175 |
176 |
177 |
178 | )
179 | }
180 | }
181 |
182 |
183 | export default DragDropContext(HTML5Backend)(ChartSettingBoard);
--------------------------------------------------------------------------------
/src/components/ConfigDropBox.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { DropTarget } from 'react-dnd';
3 | import { Icon, Popover } from 'antd'
4 | import './configDropBox.css'
5 |
6 | const ItemTypes = 'item'
7 | const itarget = {
8 | canDrop(props) {
9 | return props.canDrop(props.id);
10 | },
11 | drop(props) {
12 | props.move(props.id)
13 | }
14 | };
15 |
16 | function icollect(connect, monitor) {
17 | return {
18 | connectDropTarget: connect.dropTarget(),
19 | isOver: monitor.isOver(),
20 | canDrop: monitor.canDrop()
21 | };
22 | }
23 |
24 | export class ConfigBox extends Component {
25 |
26 | render() {
27 | const { connectDropTarget, isOver, item, canDrop } = this.props;
28 | return connectDropTarget(
29 |
30 |
{item.title}
31 |
32 | {item.dec}} trigger="hover" placement="bottom" >
33 |
34 |
35 |
36 |
37 |
38 | {this.props.children}
39 | {isOver && canDrop ?
40 |
: null
41 | }
42 | {isOver && !canDrop ?
43 | < div className='dropOver noDrop' /> : null
44 | }
45 |
46 |
47 | )
48 | }
49 | }
50 |
51 | const ConfigDropBox = DropTarget(ItemTypes, itarget, icollect)(ConfigBox)
52 |
53 | export default ConfigDropBox
54 |
--------------------------------------------------------------------------------
/src/components/DragElement.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import './dragElement.css'
3 | import { Button } from 'antd'
4 | import { DragSource } from 'react-dnd';
5 |
6 | const ItemTypes = 'item';
7 |
8 | const ItemSource = {
9 | beginDrag(props, monitor, component) {
10 | props.beginDrag(props.id)
11 | return {};
12 | },
13 |
14 | endDrag(props, monitor, component) {
15 | if (!monitor.didDrop()) {
16 | return;
17 | }
18 | props.endDrag()
19 | }
20 | };
21 |
22 | function collect(connect, monitor) {
23 | return {
24 | connectDragSource: connect.dragSource(),
25 | isDragging: monitor.isDragging()
26 | }
27 | }
28 |
29 | export class DragElement extends Component {
30 |
31 | render() {
32 | const { connectDragSource, isDragging, item } = this.props;
33 | return connectDragSource(
34 |
39 | {item.name}
40 | {item.type}
41 |
42 | )
43 | }
44 | }
45 |
46 | const DragItem = DragSource(ItemTypes, ItemSource, collect)(DragElement);
47 |
48 | export default DragItem
49 |
--------------------------------------------------------------------------------
/src/components/DropConfigChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import ReactEcharts from 'echarts-for-react';
3 |
4 | export class DropConfigChart extends Component {
5 |
6 | getOption() {
7 | const { dropConfig, chartType } = this.props;
8 | let legendData = [];
9 | const xTarget = dropConfig.filter((item) => item.key === 'xAxis')[0].items[0];
10 | const xData = xTarget ? dropConfig.filter((item) => item.key === 'xAxis')[0].items[0].data : [];
11 | const yData = dropConfig.filter((item) => item.key === 'yAxis')[0].items.map((ist) => {
12 | legendData.push(ist.name || '')
13 | const vdata = { type: ist.chart, color: ist.color, data: ist.data, name: ist.name };
14 | return vdata
15 | }
16 | )
17 |
18 | const option = {
19 | xAxis: {
20 | type: 'category',
21 | data: xData
22 | },
23 | grid: {
24 | left: '3%',
25 | right: '4%',
26 | bottom: '3%',
27 | containLabel: true
28 | },
29 | yAxis: {
30 | type: 'value'
31 | },
32 | legend: {
33 | x: 'right',
34 | data: legendData
35 | },
36 | series: yData
37 | };
38 | return option
39 | }
40 |
41 | getPieOption() {
42 | const dropConfig = this.props.dropConfig;
43 | let legendData = [];
44 | const target = dropConfig.filter((item) => item.key === 'series')[0];
45 | const seriesData = target ? target.items.map((ist) => {
46 | legendData.push(ist.name || '')
47 | const vdata = { value: ist.value, name: ist.name };
48 | return vdata
49 | }
50 | ) : [];
51 | const option = {
52 | tooltip: {
53 | trigger: 'item',
54 | formatter: "{b}: {c} ({d}%)"
55 | },
56 | legend: {
57 | x: 'right',
58 | data: legendData
59 | },
60 | series: [
61 | {
62 | type: 'pie',
63 | data: seriesData
64 | }
65 | ]
66 | };
67 |
68 | return option;
69 | }
70 |
71 | render() {
72 | const { chartType } = this.props
73 | return (
74 |
75 | {chartType === 'line' || chartType === 'bar' ? < ReactEcharts option={this.getOption()} notMerge={true} lazyUpdate={true} /> : ''}
76 | {chartType === 'pie' ? : ''}
77 |
78 | )
79 | }
80 | }
81 |
82 | export default DropConfigChart
83 |
--------------------------------------------------------------------------------
/src/components/DropElement.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Icon, Row, Col, Select, Popover } from 'antd'
3 | import { SketchPicker } from 'react-color';
4 | import './dropElement.css'
5 |
6 | const Option = Select.Option;
7 |
8 | const XItem = ({ item, delItem, pitem, pid }) => {
9 | return (
10 |
11 | {item.name}
12 | delItem(item, pitem, pid)} />
13 | {item.type}
14 |
15 | )
16 | }
17 |
18 | const YItem = ({ item, delItem, pitem, pid, changeItem }) => {
19 | const content = ( changeItem(color.hex, 'color')} />)
20 | return (
21 |
22 |
23 | {item.name}
24 |
25 |
35 |
36 |
37 |
38 | 颜色
39 |
40 | delItem(item, pitem, pid)} />
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | export class DropElement extends Component {
48 |
49 | render() {
50 | const dropItem = this.props.pitem.dropItem;
51 | return (
52 |
53 | {dropItem === 1 ? : null}
54 | {dropItem === 2 ? : null}
55 |
56 | )
57 | }
58 | }
59 |
60 | export default DropElement
61 |
--------------------------------------------------------------------------------
/src/components/chartSettingBoard.css:
--------------------------------------------------------------------------------
1 | .chartSettingBoard {
2 | margin: 30px;
3 | }
4 | .chartSettingBoard .leftBox {
5 | width: 100%;
6 | height: 230px;
7 | background-color: #fafafa;
8 | border-radius: 4px;
9 | margin-top: 10px;
10 | padding: 10px;
11 | border: solid 1px #f0f0f0;
12 | }
13 | .chartSettingBoard .icon {
14 | float: right;
15 | padding: 0 5px;
16 | cursor: pointer;
17 | line-height: 32px
18 | }
19 |
20 | .chartSettingBoard .chartTypeSelect .ant-select-selection--single {
21 | height: 50px;
22 | }
23 | .chartSettingBoard .chartTypeSelect .charIconBox {
24 | position: absolute;
25 | width: 30px;
26 | height: 30px;
27 | top: 10px;
28 | left: 0;
29 | display: block !important;
30 | font-size: 20px;
31 | color: #fff;
32 | border-radius: 3px;
33 | padding: 3px 5px;
34 | background: #0099cc
35 | }
36 | .chartSettingBoard .chartTypeSelect .ant-select-selection__rendered {
37 | line-height: 23px;
38 | padding-left: 40px
39 | }
40 | .chartSettingBoard .chartTypeSelect p {
41 | margin: 0;
42 | display: block !important
43 | }
44 |
45 | .chartSettingBoard .shortBox .configDropBox {
46 | height: 140px;
47 | margin-bottom: 10px;
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/configDropBox.css:
--------------------------------------------------------------------------------
1 | .configDropBox {
2 | height: 290px;
3 | width: 100%;
4 | position: relative;
5 | background-color: #fafafa;
6 | border-radius: 4px;
7 | border: solid 1px #f0f0f0;
8 | overflow: auto;
9 | }
10 | .configDropBox .topTitle {
11 | height: 32px;
12 | line-height: 32px;
13 | padding: 0 10px;
14 | background: #f6f6f6
15 | }
16 | .configDropBox .dropBox {
17 | width: 100%;
18 | padding: 10px;
19 | background-color: #fafafa;
20 | }
21 | .configDropBox .dropOver {
22 | position: absolute;
23 | top: 0;
24 | left: 0;
25 | height: 100%;
26 | width: 100%;
27 | z-index: 1;
28 | opacity: 0.5;
29 | background: yellow
30 | }
31 | .configDropBox .dropOver.noDrop {
32 | background: red
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/dragElement.css:
--------------------------------------------------------------------------------
1 |
2 | .dragElement {
3 | margin-top: 5px;
4 | width: 100%;
5 | padding: 0 8px;
6 | line-height: 32px;
7 | cursor: move;
8 | height: 32px;
9 | background-color: #e6fdff;
10 | border-radius: 4px;
11 | border: solid 1px #0099cc;
12 | }
13 | .dragElement .type {
14 | color: #0099cc;
15 | float: right
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/dropElement.css:
--------------------------------------------------------------------------------
1 |
2 | .dropElement .xItem {
3 | margin-top: 5px;
4 | overflow: hidden;
5 | width: 100%;
6 | padding: 0 8px;
7 | line-height: 32px;
8 | height: 32px;
9 | background-color: #fff;
10 | border-radius: 4px;
11 | border: solid 1px #0099cc;
12 | }
13 | .dropElement .yItem {
14 | margin-top: 5px;
15 | overflow: hidden;
16 | width: 100%;
17 | border: 1px solid #fff;
18 | padding-left: 5px;
19 | line-height: 32px;
20 | height: 32px;
21 | background-color: #fff;
22 | border-radius: 4px;}
23 |
24 | .dropElement .yItem .icon {
25 | padding-left: 0
26 | }
27 |
28 | .dropElement .typeSelect {
29 | width: 100%;}
30 |
31 | .dropElement .typeSelect .ant-select-arrow {
32 | right: 5px
33 | }
34 |
35 | .dropElement .ant-select-selection__rendered {
36 | margin-left: 5px;
37 | margin-right: 5px
38 | }
39 | .dropElement .ant-select-selection {
40 | border: none
41 | }
42 | .dropElement .anticon-close {
43 | margin-top: 8px;
44 | padding: 0
45 | }
46 | .dropElement .ant-select-selection-selected-value {
47 | padding: 0
48 | }
49 | .dropElement .ant-select-focused :focus {
50 | box-shadow: none !important;
51 | border: none
52 | }
53 | .dropElement .type {
54 | float: right;
55 | }
56 | .dropElement .colorLabel {
57 | position: absolute;
58 | display: block;
59 | width: 12px;
60 | left: 5px;
61 | height: 12px;
62 | top: 2px;
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/echartConfig.js:
--------------------------------------------------------------------------------
1 | const echartConfig = {
2 | line: [{
3 | title: '值轴/X',
4 | key: 'xAxis',
5 | dropItem: 1,
6 | type: 'string',
7 | length: 1,
8 | dec: 'x轴,数据类型为字符串!',
9 | items: []
10 | },
11 | {
12 | title: '值轴/Y',
13 | key: 'yAxis',
14 | dropItem: 2,
15 | type: 'value',
16 | length: 10,
17 | dec: 'y轴,数据类型为数字!',
18 | chartSelectOpt: [{
19 | name: '条形图',
20 | value: 'line'
21 | }, {
22 | name: '柱状图',
23 | value: 'bar'
24 | }],
25 | items: []
26 | }
27 | ],
28 | bar: [{
29 | title: '值轴/X',
30 | key: 'xAxis',
31 | dropItem: 1,
32 | dec: 'x轴,数据类型为字符串!',
33 | type: 'string',
34 | length: 1,
35 | items: []
36 | },
37 | {
38 | title: '值轴/Y',
39 | key: 'yAxis',
40 | dropItem: 2,
41 | type: 'value',
42 | dec: 'y轴,数据类型为字符串!',
43 | chartSelectOpt: [{
44 | name: '条形图',
45 | value: 'line'
46 | }, {
47 | name: '柱状图',
48 | value: 'bar'
49 | }],
50 | length: 10,
51 | items: []
52 | }
53 | ],
54 | pie: [{
55 | title: '数据项',
56 | key: 'series',
57 | dropItem: 1,
58 | dec: '数据类型为数字!',
59 | type: 'value',
60 | length: 10,
61 | items: []
62 | }]
63 | }
64 |
65 | export default echartConfig;
--------------------------------------------------------------------------------
/src/img/view.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingxiaoyiyio/React-Quick-eChart/00685043651498750e95583da3e11364bd8134cb/src/img/view.gif
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import registerServiceWorker from './registerServiceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------