├── .gitignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── screenshot
├── React+Redux Cycle.png
├── UI2container.png
├── code-splitting.png
├── combine-reducer.png
├── container2container.png
├── react-router.png
├── react-stack.png
└── redux-saga.png
└── src
├── App.css
├── App.js
├── actions
├── addInputAction.js
├── counterAction.js
├── fetchAction.js
└── fetchAsyncAction.js
├── components
├── Fu.js
├── GridCharts.js
├── MoreWiseCharts.js
├── SearchResults.js
├── SideMeu.js
├── WiseCharts.1.js
├── WiseCharts.css
├── WiseCharts.js
├── WiseCharts1.js
└── Zi.js
├── containers
├── AddInput.js
├── DevTools.js
├── GetList.js
├── NumCard.js
└── SagaAsy.js
├── index.css
├── index.js
├── reducers
├── addInput.js
├── counter.js
├── sagaReducer.js
└── thunkReducer.js
├── registerServiceWorker.js
├── sagas
└── sagas.js
└── store.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 沉良
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-r3-saga
2 | 可能是最简单React+Redux+Router+Redux-saga+AntD+ES6全家桶
3 |
4 | ## 运行项目(nodejs 6.0+)
5 |
6 | ```
7 | git clone https://github.com/wlc534/react-r3-saga
8 |
9 | cd react-r3-saga
10 |
11 | npm install
12 |
13 | npm start
14 |
15 |
16 | ```
17 | ## 说明
18 |
19 | > 本项目主要用于学习 react、redux、router、redux-thunk、redux-saga、AntD等从UI到数据状态管理整合
20 |
21 | > 如果觉得不错的话,您可以点右上角 "Star" 支持一下 谢谢! ^_^
22 |
23 | > 或者您可以 "follow" 一下,我会不断开源更多的有趣的项目
24 |
25 | > 如有问题请直接在 Issues 中提,或者您发现问题并有非常好的解决方案,欢迎 PR 👍
26 |
27 | > 开发环境 Windows 10 Chrome 67 nodejs v8.11.3
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-r3-saga",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "antd": "^3.6.5",
7 | "axios": "^0.19.0",
8 | "echarts": "^4.1.0",
9 | "echarts-for-react": "^2.0.14",
10 | "lodash": "^4.17.11",
11 | "react": "^16.8.6",
12 | "react-dom": "^16.8.6",
13 | "react-grid-layout": "^0.16.6",
14 | "react-loadable": "^5.4.0",
15 | "react-redux": "^5.0.7",
16 | "react-router-dom": "^4.3.1",
17 | "react-scripts": "1.1.4",
18 | "redux": "^4.0.0",
19 | "redux-logger": "^3.0.6",
20 | "redux-saga": "^0.16.0",
21 | "redux-slider-monitor": "^2.0.0-1",
22 | "redux-thunk": "^2.3.0"
23 | },
24 | "scripts": {
25 | "start": "react-scripts start",
26 | "build": "react-scripts build",
27 | "test": "react-scripts test --env=jsdom",
28 | "eject": "react-scripts eject"
29 | },
30 | "devDependencies": {
31 | "install": "^0.12.1",
32 | "redux-devtools": "^3.4.1",
33 | "redux-devtools-chart-monitor": "^1.7.0",
34 | "redux-devtools-dock-monitor": "^1.1.3",
35 | "redux-devtools-extension": "^2.13.5",
36 | "redux-devtools-filterable-log-monitor": "^0.8.0",
37 | "redux-devtools-inspector": "^0.11.3",
38 | "redux-devtools-log-monitor": "^1.4.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/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 |
--------------------------------------------------------------------------------
/screenshot/React+Redux Cycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/React+Redux Cycle.png
--------------------------------------------------------------------------------
/screenshot/UI2container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/UI2container.png
--------------------------------------------------------------------------------
/screenshot/code-splitting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/code-splitting.png
--------------------------------------------------------------------------------
/screenshot/combine-reducer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/combine-reducer.png
--------------------------------------------------------------------------------
/screenshot/container2container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/container2container.png
--------------------------------------------------------------------------------
/screenshot/react-router.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/react-router.png
--------------------------------------------------------------------------------
/screenshot/react-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/react-stack.png
--------------------------------------------------------------------------------
/screenshot/redux-saga.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlc534/react-r3-saga/18f34ce6f1d5dffb8b3b1d5f2c6c4e846928afee/screenshot/redux-saga.png
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | @import '~antd/dist/antd.css';
2 | .App {
3 | text-align: center;
4 | display: flex;
5 | justify-content: center;
6 | }
7 | .flexP{
8 | margin-top: 16px;
9 | display: flex;
10 | justify-content: space-around;
11 | }
12 | .numShow{
13 | text-align: center;
14 | font-size: 10em;
15 | line-height: 200px;
16 | height: 200px;
17 | background: aliceblue;
18 | }
19 |
20 | .App-logo {
21 | animation: App-logo-spin infinite 20s linear;
22 | height: 80px;
23 | }
24 |
25 | .App-header {
26 | background-color: #222;
27 | height: 150px;
28 | padding: 20px;
29 | color: white;
30 | }
31 |
32 | .App-title {
33 | font-size: 1.5em;
34 | }
35 |
36 | .App-intro {
37 | font-size: large;
38 | }
39 |
40 | @keyframes App-logo-spin {
41 | from { transform: rotate(0deg); }
42 | to { transform: rotate(360deg); }
43 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import './App.css';
3 | import { Card ,Alert,Spin} from 'antd';
4 | import {
5 | BrowserRouter as Router,
6 | Route,
7 | Link,
8 | Prompt,
9 | Redirect,
10 | Switch
11 | } from 'react-router-dom'
12 | import NumCardConX from './containers/NumCard'
13 | import SideMeu from './components/SideMeu'
14 | import Loadable from 'react-loadable'
15 | import Fu from './components/Fu';
16 | import WiseCharts from './components/WiseCharts'
17 | import MoreWiseCharts from './components/MoreWiseCharts'
18 | import GridCharts from './components/GridCharts';
19 | import SearchResults from './components/SearchResults';
20 |
21 | const Login = (props) => {
22 | console.log(props)
23 | return
30 |
31 | }
32 |
33 |
34 | const Message = ({
35 | match
36 | }) => {
37 | console.log(match)
38 | return (
39 |
ms
40 | { match.params.id}
41 | )
42 | }
43 | const Inbox = ({ match }) => {
44 | return (
45 |
46 |
47 |
48 |
49 |
50 | )
51 | }
52 |
53 | const data={
54 | week: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
55 | '邮件营销': [120, 132, 101, 134, 90, 230, 210],
56 | '联盟广告': [220, 182, 191, 234, 290, 330, 310],
57 | '视频广告': [150, 232, 201, 154, 190, 330, 410],
58 | '直接访问': [320, 332, 301, 334, 390, 330, 320],
59 | '搜索引擎': [820, 932, 901, 934, 1290, 1330, 1320],
60 | }
61 |
62 | const Loading=()=>{
63 | return
64 | }
65 |
66 |
67 | const AddInputCon = Loadable({
68 | loader: () =>
69 | import ('./containers/AddInput'),
70 | loading: Loading
71 | })
72 | const GetListCon = Loadable({
73 | loader: () =>
74 | import ('./containers/GetList'),
75 | loading: Loading
76 | })
77 | const SagaAsyCon = Loadable({
78 | loader: () =>
79 | import ('./containers/SagaAsy'),
80 | loading: Loading
81 | })
82 |
83 |
84 |
85 |
86 |
87 | class App extends Component {
88 | render() {
89 | return (
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
} />
102 | } />
103 |
104 |
105 | }/>
106 |
107 |
108 | );
109 | }
110 |
111 | }
112 |
113 | export default App;
--------------------------------------------------------------------------------
/src/actions/addInputAction.js:
--------------------------------------------------------------------------------
1 | const ADD_INPUT_ACTION = 'ADD_INPUT';
2 | const STRING = 'STRING';
3 | export const addInput = (text) => {
4 | return {
5 | type: ADD_INPUT_ACTION,
6 | text: text
7 | }
8 | }
9 |
10 | export const showText = (text) => {
11 | return {
12 | type: STRING,
13 | string: text
14 | }
15 | }
--------------------------------------------------------------------------------
/src/actions/counterAction.js:
--------------------------------------------------------------------------------
1 | export const ADD_ACTION = 'ADD';
2 | export const INCREMENT_ASYNC_ACTION = 'INCREMENT_ASYNC';
3 | export const MINUS_ACTION = 'MINUS';
4 |
5 | export const addOne = () => {
6 | return {
7 | type: ADD_ACTION
8 | }
9 | }
10 |
11 | export const addOneAsync = () => {
12 | return {
13 | type: INCREMENT_ASYNC_ACTION
14 | }
15 | }
16 | export const minusOne = () => {
17 | return {
18 | type: MINUS_ACTION
19 | }
20 | }
--------------------------------------------------------------------------------
/src/actions/fetchAction.js:
--------------------------------------------------------------------------------
1 | const FETCH_REQUEST = 'FETCH_REQUEST';
2 | const FETCH_FAILURE = 'FETCH_FAILURE';
3 | const FETCH_SUCCESS = 'FETCH_SUCCESS';
4 |
5 | export const fetchDataRequest = () => {
6 | return {
7 | type: FETCH_REQUEST,
8 | status: 'loading'
9 | }
10 | }
11 | export const fetchDataFailure = (error) => {
12 | return {
13 | type: FETCH_FAILURE,
14 | error
15 | }
16 | }
17 | export const fetchDataSuccess = (result) => {
18 | return {
19 | type: FETCH_SUCCESS,
20 | result
21 | }
22 | }
23 |
24 | //redux-thunk 版通过不同 name值 如good job share ask dev
25 | export const fetchData = (name) => {
26 | return (dispatch) => {
27 | const apiUrl = `https://cnodejs.org/api/v1/topics?tab=${name}`;
28 | dispatch(fetchDataRequest())
29 | return fetch(apiUrl).then((response) => {
30 | if (response.status !== 200) {
31 | throw new Error('Fail to get response with status ' + response.status)
32 | }
33 | response.json().then((responseJson) => {
34 | console.log(responseJson)
35 | dispatch(fetchDataSuccess(responseJson.data));
36 | }).catch((error) => {
37 | dispatch(fetchDataFailure(error))
38 | })
39 | }).catch((error) => {
40 | dispatch(fetchDataFailure(error));
41 | })
42 |
43 | }
44 | }
--------------------------------------------------------------------------------
/src/actions/fetchAsyncAction.js:
--------------------------------------------------------------------------------
1 | const FETCH_ASYNC_REQUEST = 'FETCH_ASYNC_REQUEST';
2 | const FETCH_ASYNC_FAILURE = 'FETCH_ASYNC_FAILURE';
3 | const FETCH_ASYNC_SUCCESS = 'FETCH_ASYNC_SUCCESS';
4 |
5 | export const fetchDataRequest = (name) => {
6 | return {
7 | type: FETCH_ASYNC_REQUEST,
8 | name
9 | }
10 | }
11 | export const fetchDataFailure = (error) => {
12 | return {
13 | type: FETCH_ASYNC_FAILURE,
14 | error
15 | }
16 | }
17 | export const fetchDataSuccess = (result) => {
18 | return {
19 | type: FETCH_ASYNC_SUCCESS,
20 | result
21 | }
22 | }
--------------------------------------------------------------------------------
/src/components/Fu.js:
--------------------------------------------------------------------------------
1 | import React,{ Component} from 'react'
2 | import Zi from './Zi'
3 | export default class Fu extends Component{
4 | constructor(props){
5 | super(props)
6 | this.handleChange=this.handleChange.bind(this)
7 | this.state={number:0}
8 | }
9 | handleChange(){
10 | console.log('父组件中的handleChange执行')
11 | this.setState((prevState)=>({
12 | number:prevState.number+1
13 | }))
14 | }
15 | render(){
16 | return (
17 |
18 |
父组件{this.state.number}
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/src/components/GridCharts.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import WiseCharts from './WiseCharts'
3 | import GridLayout from 'react-grid-layout';
4 | import _ from "lodash";
5 | import { Responsive, WidthProvider } from "react-grid-layout";
6 | import '../../node_modules/react-grid-layout/css/styles.css'
7 | import '../../node_modules/react-resizable/css/styles.css'
8 | const ResponsiveReactGridLayout = WidthProvider(Responsive);
9 | const data={
10 | week: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
11 | '邮件营销': [120, 132, 101, 134, 90, 230, 210],
12 | '联盟广告': [220, 182, 191, 234, 290, 330, 310],
13 | '视频广告': [150, 232, 201, 154, 190, 330, 410],
14 | '直接访问': [320, 332, 301, 334, 390, 330, 320],
15 | '搜索引擎': [820, 932, 901, 934, 1290, 1330, 1320],
16 | }
17 | function generateLayout() {
18 | return _.map(_.range(0, 4), function(item, i) {
19 | var y = Math.ceil(Math.random() * 10) + 1;
20 | return {
21 | x: (_.random(0, 5) * 3) % 12,
22 | y: Math.floor(i / 6) * y,
23 | w: 3,
24 | h: y,
25 | i: i.toString(),
26 | static: Math.random() < 0.05
27 | };
28 | });
29 | }
30 |
31 | class GridCharts extends Component{
32 | static defaultProps = {
33 | className: "layout",
34 | rowHeight: 30,
35 | onLayoutChange: function() {},
36 | cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
37 | initialLayout: generateLayout()
38 | };
39 |
40 | state = {
41 | currentBreakpoint: "lg",
42 | compactType: "vertical",
43 | mounted: false,
44 | layouts: { lg: this.props.initialLayout }
45 | };
46 |
47 | componentDidMount() {
48 | this.setState({ mounted: true });
49 | }
50 |
51 | generateDOM() {
52 | return _.map(this.state.layouts.lg, function(l, i) {
53 | return (
54 |
55 | {/* {l.static ? (
56 |
60 | Static - {i}
61 |
62 | ) : (
63 | {i}
64 | )} */}
65 |
66 |
67 | );
68 | });
69 | }
70 |
71 | onBreakpointChange = breakpoint => {
72 | this.setState({
73 | currentBreakpoint: breakpoint
74 | });
75 | };
76 |
77 | onCompactTypeChange = () => {
78 | const { compactType: oldCompactType } = this.state;
79 | const compactType =
80 | oldCompactType === "horizontal"
81 | ? "vertical"
82 | : oldCompactType === "vertical" ? null : "horizontal";
83 | this.setState({ compactType });
84 | };
85 |
86 | onLayoutChange = (layout, layouts) => {
87 | this.props.onLayoutChange(layout, layouts);
88 | };
89 |
90 | onNewLayout = () => {
91 | this.setState({
92 | layouts: { lg: generateLayout() }
93 | });
94 | };
95 |
96 | render() {
97 | return (
98 |
99 | {/*
100 | Current Breakpoint: {this.state.currentBreakpoint} ({
101 | this.props.cols[this.state.currentBreakpoint]
102 | }{" "}
103 | columns)
104 |
105 |
106 | Compaction type:{" "}
107 | {_.capitalize(this.state.compactType) || "No Compaction"}
108 |
*/}
109 |
110 |
113 |
126 | {this.generateDOM()}
127 |
128 |
129 | );
130 | }
131 | }
132 | // render() {
133 | // // layout is an array of objects, see the demo for more complete usage
134 | // var layout = [
135 | // {i: 'a', x: 0, y: 0, w: 8, h: 2, },
136 | // {i: 'b', x: 0, y: 0, w: 3, h: 2, minW: 2, maxW: 4},
137 | // {i: 'c', x: 3, y: 0, w: 5, h: 2},
138 | // {i: 'd', x: 3, y: 0, w: 5, h: 2}
139 | // ];
140 | // var style={background:'#ccc',border:'1px solid black'};
141 | // return (
142 | //
143 | //
144 | //
145 | //
146 | //
147 | //
148 | //
149 | //
150 | //
151 | //
152 | //
153 | //
154 | //
155 | //
156 | //
157 | //
158 |
159 | // )
160 | // }
161 | // }
162 | export default GridCharts;
163 |
164 |
--------------------------------------------------------------------------------
/src/components/MoreWiseCharts.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component
3 | } from 'react';
4 |
5 | import ReactEcharts from 'echarts-for-react';
6 | import echarts from 'echarts'
7 | import './WiseCharts.css'
8 |
9 | const getKeys = data => Object.keys(data);
10 |
11 | export default class MoreWiseCharts extends Component {
12 | static defaultProps = {
13 | height: 400,
14 | title: '默认名称',
15 | type: 'line'
16 | };
17 |
18 | constructor(props) {
19 | super(props);
20 | this.getOption = this.getOption.bind(this);
21 | }
22 | componentDidMount(){
23 | const echartsInstanceOne=this.echarts_react_one.getEchartsInstance()
24 | const echartsInstancetwo=this.echarts_react_two.getEchartsInstance()
25 | echarts.connect([echartsInstanceOne,echartsInstancetwo])
26 | }
27 |
28 | getOption() {
29 | setTimeout(() => {
30 | console.log(this.echarts_react)
31 | }, 1000);
32 | const [first, ...legendData] = getKeys(this.props.data);
33 | const legendDataArr = [];
34 | let legendTrStr = '';
35 | legendData.forEach(item => {
36 | legendDataArr.push({
37 | name: item,
38 | icon: 'circle'
39 | });
40 | legendTrStr += `${item} | `;
41 |
42 | });
43 |
44 | return {
45 | title: {
46 | text: this.props.title,
47 | },
48 | tooltip: {
49 | trigger: 'axis'
50 | },
51 | legend: {
52 | bottom: '3%',
53 | type: 'scroll',
54 | data: legendDataArr
55 | },
56 |
57 | dataset: {
58 | source: this.props.data
59 | },
60 | grid: {
61 | left: '3%',
62 | right: '4%',
63 | bottom: '10%',
64 | containLabel: true
65 | },
66 |
67 | toolbox: {
68 | show: true,
69 | feature: {
70 | dataZoom: {
71 | yAxisIndex: 'none'
72 | },
73 | dataView: {
74 | readOnly: false,
75 | optionToContent: function otc(opt) {
76 | const timeArr = Object.values(opt.dataset[0].source);
77 | let table = `
78 | 时间 | ${legendTrStr}
79 |
`;
80 | for (let i = 0, l = timeArr[0].length; i < l; i += 1) {
81 | let tdElm = ``;
82 | for (let j = 1; j < timeArr.length; j += 1) {
83 | const element = timeArr[j][i];
84 | tdElm += `${element} | `;
85 | }
86 | table += `${timeArr[0][i]} | ${tdElm}
87 |
`;
88 | }
89 | table += '
';
90 | return table;
91 | },
92 | },
93 | magicType: {
94 | type: ['line', 'bar']
95 | },
96 | restore: {},
97 | saveAsImage: {}
98 | }
99 |
100 | },
101 | xAxis: {
102 | type: 'category',
103 | boundaryGap: false,
104 | },
105 | yAxis: {
106 | type: 'value'
107 | },
108 | series: [...legendData].fill({
109 | type: this.props.type
110 | }),
111 | color: [
112 | '#FF9C6E', '#FFC069', '#95DE64', '#5CDBD3', '#69C0FF', '#85A5FF', '#B37FEB', '#FF85C0'
113 |
114 | ]
115 | };
116 |
117 |
118 |
119 | }
120 |
121 |
122 |
123 | render() {
124 | // const echarts_instance = this.echarts_react.getEchartsInstance();
125 | // console.info(echarts_instance)
126 | // console.log(this.echarts_react)
127 | return (
128 |
129 | { this.echarts_react_one = e; }}
138 | className = 'react_for_echarts' / >
139 | { this.echarts_react_two = e; }}
148 | className = 'react_for_echarts' / >
149 |
150 |
151 |
152 | )}
153 | }
--------------------------------------------------------------------------------
/src/components/SearchResults.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { Input } from 'antd';
3 | import axios from 'axios';
4 |
5 | const { Search } = Input;
6 |
7 | function SearchResults() {
8 | const [data, setData] = useState({ hits: [] });
9 | const [query, setQuery] = useState('react');
10 |
11 | useEffect(() => {
12 | let ignore = false;
13 | console.log(query)
14 |
15 | async function fetchData() {
16 | const result = await axios('https://hn.algolia.com/api/v1/search?query=' + query);
17 | if (!ignore) setData(result.data);
18 | }
19 |
20 | fetchData();
21 | return () => { ignore = true; }
22 | }, [query]);
23 |
24 | const getData=(value)=>{
25 | async function fetch() {
26 | const json=await axios ('https://hn.algolia.com/api/v1/search?query=' + value);
27 | setData(json.data);
28 | console.log(json.data);
29 | }
30 | fetch();
31 | }
32 |
33 | return (
34 |
55 | );
56 | }
57 |
58 | export default SearchResults;
59 |
60 |
61 |
--------------------------------------------------------------------------------
/src/components/SideMeu.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Link} from 'react-router-dom'
3 |
4 | const SideMeu=()=>(
5 |
6 |
21 |
22 | )
23 |
24 | export default SideMeu
--------------------------------------------------------------------------------
/src/components/WiseCharts.1.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import ReactEcharts from 'echarts-for-react';
3 | export default class WiseCharts extends Component{
4 | constructor(props){
5 | super(props);
6 | this.getOption=this.getOption.bind(this);
7 |
8 | }
9 | getOption(){
10 | const source={
11 | week:['周一','周二','周三','周四','周五','周六','周日'],
12 | '邮件营销':[120, 132, 101, 134, 90, 230, 210],
13 | '联盟广告':[220, 182, 191, 234, 290, 330, 310],
14 | '视频广告':[150, 232, 201, 154, 190, 330, 410],
15 | '直接访问':[320, 332, 301, 334, 390, 330, 320],
16 | '搜索引擎':[820, 932, 901, 934, 1290, 1330, 1320],
17 |
18 | };
19 | return {
20 | title: {
21 | text: '多折线图'
22 | },
23 | tooltip: {
24 | trigger: 'axis'
25 | },
26 | legend: {
27 | bottom: '3%',
28 | data:[
29 | {name:'邮件营销',icon:'circle'},
30 | {name:'联盟广告',icon:'circle'},
31 | {name:'视频广告',icon:'circle'},
32 | {name:'直接访问',icon:'circle'},
33 | {name:'搜索引擎',icon:'circle'},
34 | ]
35 | },
36 | dataset:{
37 | source
38 |
39 | },
40 | grid: {
41 | left: '3%',
42 | right: '4%',
43 | bottom: '10%',
44 | containLabel: true
45 | },
46 | toolbox: {
47 | show: true,
48 | feature: {
49 | dataZoom: {
50 | yAxisIndex: 'none'
51 | },
52 | dataView: {readOnly: false},
53 | magicType: {type: ['line', 'bar']},
54 | restore: {},
55 | saveAsImage: {}
56 | }
57 | },
58 | xAxis: {
59 | type: 'category',
60 | boundaryGap: false,
61 |
62 | },
63 | yAxis: {
64 | type: 'value'
65 | },
66 | series: [
67 | {
68 | type:'line'
69 | },
70 | {
71 | type:'line'
72 | },
73 | {
74 | type:'line'
75 | },
76 | {
77 | type:'line'
78 | },
79 | {
80 | type:'line'
81 | },
82 |
83 | ],
84 | color: [
85 | '#FF9C6E', '#FFC069', '#95DE64', '#5CDBD3', '#69C0FF', '#85A5FF', '#B37FEB', '#FF85C0'
86 | ]
87 | };
88 |
89 | }
90 | render(){
91 | return (
92 | < ReactEcharts
93 | option = {
94 | this.getOption()
95 | }
96 | style = {
97 | {
98 | height: '350px',
99 | width: '60%'
100 | }
101 | }
102 | className = 'react_for_echarts' / >
103 | )
104 |
105 | }
106 | }
--------------------------------------------------------------------------------
/src/components/WiseCharts.css:
--------------------------------------------------------------------------------
1 | .reference {
2 | border-collapse: collapse;
3 | width: 100%;
4 | margin-bottom: 4px;
5 | margin-top: 4px;
6 | }
7 |
8 | table.reference tr {
9 | height: 40px;
10 | border-bottom: 1px solid #e8e8e8;
11 | }
12 | table.reference tr:nth-child(odd) {
13 | background-color: #f6f4f0;
14 | }
15 | table.reference tr:hover {
16 | background-color: #ff9c6e;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/WiseCharts.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component
3 | } from 'react';
4 |
5 | import ReactEcharts from 'echarts-for-react';
6 | import './WiseCharts.css'
7 |
8 | const getKeys = data => Object.keys(data);
9 |
10 | export default class WiseCharts extends Component {
11 | static defaultProps = {
12 | height: 400,
13 | width:'60%',
14 | title: '默认名称',
15 | type: 'line'
16 | };
17 |
18 | constructor(props) {
19 | super(props);
20 | this.getOption = this.getOption.bind(this);
21 | }
22 |
23 | getOption() {
24 | const [first, ...legendData] = getKeys(this.props.data);
25 | const legendDataArr = [];
26 | let legendTrStr = '';
27 | legendData.forEach(item => {
28 | legendDataArr.push({
29 | name: item,
30 | icon: 'circle'
31 | });
32 | legendTrStr += `${item} | `;
33 |
34 | });
35 |
36 | return {
37 | title: {
38 | text: this.props.title,
39 | },
40 | tooltip: {
41 | trigger: 'axis'
42 | },
43 | legend: {
44 | bottom: '3%',
45 | type: 'scroll',
46 | data: legendDataArr
47 | },
48 |
49 | dataset: {
50 | source: this.props.data
51 | },
52 | grid: {
53 | left: '3%',
54 | right: '4%',
55 | bottom: '10%',
56 | containLabel: true
57 | },
58 |
59 | toolbox: {
60 | show: true,
61 | feature: {
62 | dataZoom: {
63 | yAxisIndex: 'none'
64 | },
65 | dataView: {
66 | readOnly: false,
67 | optionToContent: function otc(opt) {
68 | const timeArr = Object.values(opt.dataset[0].source);
69 | let table = `
70 | 时间 | ${legendTrStr}
71 |
`;
72 | for (let i = 0, l = timeArr[0].length; i < l; i += 1) {
73 | let tdElm = ``;
74 | for (let j = 1; j < timeArr.length; j += 1) {
75 | const element = timeArr[j][i];
76 | tdElm += `${element} | `;
77 | }
78 | table += `${timeArr[0][i]} | ${tdElm}
79 |
`;
80 | }
81 | table += '
';
82 | return table;
83 | },
84 | },
85 | magicType: {
86 | type: ['line', 'bar']
87 | },
88 | restore: {},
89 | saveAsImage: {}
90 | }
91 |
92 | },
93 | xAxis: {
94 | type: 'category',
95 | boundaryGap: false,
96 | },
97 | yAxis: {
98 | type: 'value'
99 | },
100 | series: [...legendData].fill({
101 | type: this.props.type
102 | }),
103 | color: [
104 | '#FF9C6E', '#FFC069', '#95DE64', '#5CDBD3', '#69C0FF', '#85A5FF', '#B37FEB', '#FF85C0'
105 |
106 | ]
107 | };
108 |
109 |
110 |
111 | }
112 |
113 |
114 |
115 | render() {
116 | let {width,height}=this.props;
117 | return (
118 |
127 | )}
128 | }
--------------------------------------------------------------------------------
/src/components/WiseCharts1.js:
--------------------------------------------------------------------------------
1 | import React,{Component} from 'react';
2 | import ReactEcharts from 'echarts-for-react';
3 | import {Icon} from 'antd';
4 | export default class WiseCharts extends Component{
5 | constructor(props){
6 | super(props);
7 | this.getOption=this.getOption.bind(this);
8 |
9 | }
10 | getOption(){
11 | return {
12 | title: {
13 | text: '折线图堆叠'
14 | },
15 | tooltip: {
16 | trigger: 'axis'
17 | },
18 | legend: {
19 | // data:['邮件营销','联盟广告','视频广告','直接访问','搜索引擎']
20 | bottom:'3%',
21 | data:[
22 | {name:'邮件营销',icon:'circle'},
23 | {name:'联盟广告',icon:'circle'},
24 | {name:'视频广告',icon:'circle'},
25 | {name:'直接访问',icon:'circle'},
26 | {name:'搜索引擎',icon:'circle'},
27 | ]
28 | },
29 | grid: {
30 | left: '3%',
31 | right: '4%',
32 | bottom: '10%',
33 | containLabel: true
34 | },
35 | toolbox: {
36 | show: true,
37 | feature: {
38 | dataZoom: {
39 | yAxisIndex: 'none'
40 | },
41 | dataView: {readOnly: false},
42 | magicType: {type: ['line', 'bar']},
43 | restore: {},
44 | saveAsImage: {}
45 | }
46 | },
47 | xAxis: {
48 | type: 'category',
49 | boundaryGap: false,
50 | data: ['周一','周二','周三','周四','周五','周六','周日']
51 | },
52 | yAxis: {
53 | type: 'value'
54 | },
55 | series: [
56 | {
57 | name:'邮件营销',
58 | type:'line',
59 | data:[120, 132, 101, 134, 90, 230, 210]
60 | },
61 | {
62 | name:'联盟广告',
63 | type:'line',
64 | data:[220, 182, 191, 234, 290, 330, 310]
65 | },
66 | {
67 | name:'视频广告',
68 | type:'line',
69 | data:[150, 232, 201, 154, 190, 330, 410]
70 | },
71 | {
72 | name:'直接访问',
73 | type:'line',
74 | data:[320, 332, 301, 334, 390, 330, 320]
75 | },
76 | {
77 | name:'搜索引擎',
78 | type:'line',
79 | data:[820, 932, 901, 934, 1290, 1330, 1320]
80 | }
81 | ],
82 | color: [
83 | '#FF9C6E', '#FFC069', '#95DE64', '#5CDBD3', '#69C0FF', '#85A5FF', '#B37FEB', '#FF85C0'
84 | ]
85 | };
86 |
87 | }
88 | render(){
89 | return (
90 | < ReactEcharts
91 | option = {
92 | this.getOption()
93 | }
94 | style = {
95 | {
96 | height: '350px',
97 | width: '60%'
98 | }
99 | }
100 | className = 'react_for_echarts' / >
101 | )
102 |
103 | }
104 | }
--------------------------------------------------------------------------------
/src/components/Zi.js:
--------------------------------------------------------------------------------
1 | import React,{ Component} from 'react'
2 | export default class Zi extends Component{
3 | constructor(props) {
4 | super(props)
5 | this.clickChange = this.clickChange.bind(this)
6 |
7 | }
8 | clickChange() {
9 | this.props.changeData()
10 | }
11 |
12 |
13 | render(){
14 | return (
15 | 子组件点击{this.props.num}次
16 | )
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/src/containers/AddInput.js:
--------------------------------------------------------------------------------
1 | import React, {
2 | Component
3 | } from 'react'
4 | import {
5 | Card,
6 | Input
7 | } from 'antd'
8 | import {
9 | connect
10 | } from 'react-redux';
11 | import {
12 | addInput,
13 | showText
14 | } from "../actions/addInputAction";
15 |
16 |
17 | class AddInput extends Component {
18 | constructor(props) {
19 | super(props)
20 | this.handleClick = this.handleClick.bind(this)
21 | this.handleAddLeft = this.handleAddLeft.bind(this)
22 | }
23 | handleClick(value) {
24 | this.props.onAddInput(value)
25 | }
26 | handleAddLeft(value) {
27 | this.props.onShowText(value)
28 | }
29 |
30 | render() {
31 | const {
32 | showtext
33 | } = this.props;
34 | return (
35 |
36 | { showtext}
37 |
38 | this.handleClick(value)}/>
41 | < Input.Search placeholder = "input search text"
42 | enterButton = "到左边"
43 | onSearch = {value => this.handleAddLeft(value)}/>
44 |
45 |
46 |
47 | )
48 | }
49 |
50 | }
51 |
52 | function mapStateToProps(state) {
53 | return {
54 | showtext: state.addInput.showtext
55 | }
56 |
57 | }
58 |
59 | function mapDispatchToProps(dispatch) {
60 | return {
61 | onAddInput: (text) => dispatch(addInput(text)),
62 | onShowText: (str) => dispatch(showText(str))
63 | }
64 |
65 | }
66 |
67 | const AddInputCon = connect(
68 | mapStateToProps,
69 | mapDispatchToProps
70 | )(AddInput)
71 |
72 | export default AddInputCon
--------------------------------------------------------------------------------
/src/containers/DevTools.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | // Exported from redux-devtools
4 | import { createDevTools } from 'redux-devtools';
5 |
6 | // Monitors are separate packages, and you can make a custom one
7 | import LogMonitor from 'redux-devtools-log-monitor';
8 | import DockMonitor from 'redux-devtools-dock-monitor';
9 | import ChartMonitor from 'redux-devtools-chart-monitor';
10 | import SliderMonitor from 'redux-slider-monitor';
11 | import Inspector from 'redux-devtools-inspector';
12 | import FilterableLogMonitor from 'redux-devtools-filterable-log-monitor'
13 |
14 |
15 |
16 |
17 |
18 |
19 | // createDevTools takes a monitor and produces a DevTools component
20 | const DevTools = createDevTools(
21 | // Monitors are individually adjustable with props.
22 | // Consult their repositories to learn about those props.
23 | // Here, we put LogMonitor inside a DockMonitor.
24 | // Note: DockMonitor is visible by default.
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 |
38 | export default DevTools;
--------------------------------------------------------------------------------
/src/containers/GetList.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Card,Input,Spin,List} from 'antd'
3 | import { connect} from 'react-redux';
4 | import {fetchData} from "../actions/fetchAction";
5 |
6 | class GetList extends Component {
7 | constructor(props) {
8 | super(props)
9 | this.handleClick = this.handleClick.bind(this)
10 | }
11 | handleClick(value) {
12 | this.props.onSearchByName(value)
13 | }
14 | render(){
15 | const {arrData,status } =this.props;
16 | // const items=arrData.map(item=>
17 | // {item.title}
18 | // )
19 | return (
20 |
21 |
22 | this.handleClick(value)}
26 | />
27 |
28 | {status=='loading'?:""}
29 | ({item.title})}
33 | />
34 |
35 |
36 |
37 | )
38 | }
39 |
40 | }
41 |
42 | function mapStateToProps(state) {
43 | console.log(state.thunkReducer.data)
44 | return {
45 | arrData: state.thunkReducer.data,
46 | status: state.thunkReducer.status
47 | }
48 |
49 | }
50 |
51 | function mapDispatchToProps(dispatch) {
52 | return {
53 | onSearchByName: (name) => dispatch(fetchData(name)),
54 | }
55 |
56 | }
57 |
58 | const GetListCon = connect(
59 | mapStateToProps,
60 | mapDispatchToProps
61 | )(GetList)
62 |
63 | export default GetListCon
--------------------------------------------------------------------------------
/src/containers/NumCard.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Card, Button} from 'antd'
3 | import { connect} from 'react-redux';
4 | // import {addOne,addOneAsync} from "../actions/counterAction";
5 | import * as CounterActions from '../actions/counterAction'
6 |
7 |
8 | class NumCard extends Component {
9 | constructor(props) {
10 | super(props)
11 |
12 | }
13 | componentDidMount() {
14 | const {
15 | dispatch
16 | } = this.props
17 | console.log("dispatch", dispatch)
18 | console.log("this.props", this.props)
19 | }
20 | render(){
21 | const {count ,strText,addOne,minusOne,addOneAsync,onDecreaseClick} =this.props;
22 | console.log("this.props",this.props.dispatch)
23 | return (
24 |
25 | {count}
26 | {strText}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | }
38 |
39 | function mapStateToProps(state) {
40 | console.log(state)
41 | return {
42 | count: state.counter.count,
43 | secCount: state.counter.secCount,
44 | strText: state.counter.strText
45 | }
46 |
47 | }
48 | // function mapDispatchToProps(dispatch) {
49 | // console.info(dispatch)
50 | // return {
51 | // onIncreaseClick:()=>dispatch(addOne()),
52 | // onDecreaseClick:()=>dispatch(minusOne())
53 | // }
54 |
55 | // }
56 |
57 | // const NumCardCon= connect(
58 | // mapStateToProps,
59 | // {addOne,minusOne,addOneAsync}
60 | // )(NumCard)
61 |
62 | // export default NumCardCon
63 | // export default connect(
64 | // mapStateToProps,
65 | // {addOne,minusOne,addOneAsync}
66 |
67 | // )(NumCard)
68 | export default connect(mapStateToProps,{...CounterActions})(NumCard)
--------------------------------------------------------------------------------
/src/containers/SagaAsy.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Card, Input,Spin,List} from 'antd'
3 | import { connect} from 'react-redux';
4 | import {fetchDataRequest} from "../actions/fetchAsyncAction";
5 |
6 |
7 |
8 | class SagaAsy extends Component {
9 | constructor(props) {
10 | super(props)
11 | this.handleClick = this.handleClick.bind(this)
12 | }
13 | handleClick(value) {
14 | this.props.onSearchByName(value)
15 | console.log(value)
16 |
17 | }
18 |
19 | render(){
20 | const {arrData,status } =this.props;
21 |
22 | return (
23 |
24 |
25 | this.handleClick(value)}
29 | />
30 |
31 | {status=='loading'?:""}
32 | ({item.title})}
36 | />
37 |
38 |
39 | )
40 | }
41 |
42 | }
43 |
44 | function mapStateToProps(state) {
45 | console.log(state.sagaReducer)
46 | return {
47 | arrData: state.sagaReducer.data,
48 | status: state.sagaReducer.status
49 | }
50 |
51 | }
52 |
53 | function mapDispatchToProps(dispatch) {
54 | return {
55 | onSearchByName: (name) => dispatch(fetchDataRequest(name)),
56 | }
57 |
58 | }
59 |
60 | const SagaAsyCon = connect(
61 | mapStateToProps,
62 | mapDispatchToProps
63 | )(SagaAsy)
64 |
65 | export default SagaAsyCon
--------------------------------------------------------------------------------
/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 { Provider } from 'react-redux';
4 | import './index.css';
5 | import App from './App';
6 | import store from './store'
7 | import registerServiceWorker from './registerServiceWorker';
8 | import DevTools from './containers/DevTools';
9 |
10 |
11 | ReactDOM.render(
12 |
13 |
14 | {/*
方式Ⅰ 放开注释*/}
15 |
16 |
17 | ,
18 | document.getElementById('root'));
19 | registerServiceWorker();
--------------------------------------------------------------------------------
/src/reducers/addInput.js:
--------------------------------------------------------------------------------
1 | function addInput(state = {
2 | showtext: '沉良'
3 | }, action) {
4 | const showtext = state.showtext
5 | console.log(showtext)
6 | switch (action.type) {
7 | case 'ADD_INPUT':
8 | return {
9 | ...state,
10 | showtext: action.text
11 | }
12 | default:
13 | return state
14 | }
15 |
16 | }
17 |
18 | export default addInput;
--------------------------------------------------------------------------------
/src/reducers/counter.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_ACTION,
3 | ADD_ACTION_SUCCESS
4 | } from '../actions/counterAction';
5 |
6 | function counter(state = {
7 | count: 0,
8 | secCount: 0
9 | }, action) {
10 | const count = state.count
11 | console.log(action)
12 | switch (action.type) {
13 | case 'ADD':
14 | return { ...state,
15 | count: count + 1,
16 | secCount: `${count+1}+`
17 | }
18 | case 'MINUS':
19 | return { ...state,
20 | count: count - 1,
21 | secCount: `${count-1}-`
22 | }
23 | case 'STRING':
24 | return { ...state,
25 | strText: action.string
26 | }
27 | default:
28 | return state
29 | }
30 |
31 | }
32 |
33 | export default counter;
--------------------------------------------------------------------------------
/src/reducers/sagaReducer.js:
--------------------------------------------------------------------------------
1 | function sagaReducer(state = {
2 | data: []
3 | }, action) {
4 | console.log(action)
5 | switch (action.type) {
6 | case 'FETCH_ASYNC_SUCCESS':
7 | return { ...state,
8 | data: action.result,
9 | status: 'success'
10 | }
11 | case 'FETCH_ASYNC_FAILURE':
12 | return { ...state,
13 | status: 'failure'
14 | }
15 | case 'FETCH_ASYNC_REQUEST':
16 | return { ...state,
17 | status: 'loading'
18 | }
19 | default:
20 | return state
21 | }
22 |
23 | }
24 |
25 | export default sagaReducer;
--------------------------------------------------------------------------------
/src/reducers/thunkReducer.js:
--------------------------------------------------------------------------------
1 | function thunkReducer(state = {
2 | data: []
3 | }, action) {
4 | console.log(action)
5 | switch (action.type) {
6 | case 'FETCH_SUCCESS':
7 | return { ...state,
8 | data: action.result,
9 | status: 'success'
10 | }
11 | case 'FETCH_FAILURE':
12 | return { ...state,
13 | status: 'failure'
14 | }
15 | case 'FETCH_REQUEST':
16 | return { ...state,
17 | status: 'loading'
18 | }
19 | default:
20 | return state
21 | }
22 |
23 | }
24 |
25 | export default thunkReducer;
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/sagas/sagas.js:
--------------------------------------------------------------------------------
1 | import {takeLatest,all,takeEvery,put,call,take,fork} from 'redux-saga/effects'
2 | import {fetchDataFailure,fetchDataRequest,fetchDataSuccess} from '../actions/fetchAsyncAction'
3 | import {ADD_ACTION, ADD_ACTION_SUCCESS,INCREMENT_ASYNC,INCREMENT_ASYNC_ACTION,addOneAsync,addOne} from '../actions/counterAction';
4 | import {delay} from 'redux-saga'
5 |
6 |
7 |
8 | export default function* rootSaga () {
9 | // while (true) {
10 | // yield watchIncrementAsync()
11 | // yield watchGetDataByType()
12 | // }
13 | yield all([
14 | watchIncrementAsync(),
15 | watchGetDataByType()
16 | ])
17 | //
18 | }
19 |
20 | // export async function incrementAsync(){
21 | // await delay(1000)
22 | // await put(addOneAsync());
23 | // }
24 | export function* incrementAsync() {
25 | console.log('>>>>>incrementAsync')
26 | yield delay(1000)
27 | // yield put(addOneAsync());
28 | yield put(addOne());
29 | }
30 |
31 | export function* watchIncrementAsync() {
32 | yield takeEvery('INCREMENT_ASYNC', incrementAsync)
33 | }
34 |
35 | //用saga异步获取后台数据
36 | function fetchCnData(name) {
37 | return fetch(`https://cnodejs.org/api/v1/topics?tab=${name}`).then((response) => {
38 | if (response.status !== 200) {
39 | throw new Error('Fail to get response with status ' + response.status)
40 | }
41 | return response.json()
42 | })
43 |
44 | }
45 | // 1.worker
46 | export function* getDataByType(name) {
47 | try {
48 | const response = yield call(fetchCnData, name);
49 | yield put(fetchDataSuccess(response.data))
50 | } catch (error) {
51 | yield put(fetchDataFailure(error))
52 | }
53 | }
54 | // 2.watcher
55 | export function* watchGetDataByType() {
56 | while (true) {
57 | const {
58 | name
59 | } = yield take('FETCH_ASYNC_REQUEST');
60 | yield fork(getDataByType, name)
61 | }
62 |
63 |
64 | }
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import {
2 | createStore,
3 | combineReducers,
4 | applyMiddleware,
5 | compose
6 | } from 'redux'
7 | import createSagaMiddleware from 'redux-saga'
8 | import counter from './reducers/counter'
9 | import addInput from './reducers/addInput'
10 | import thunkReducer from './reducers/thunkReducer'
11 | import sagaReducer from './reducers/sagaReducer'
12 | import logger from 'redux-logger'
13 | import thunk from 'redux-thunk'
14 | import rootSaga from './sagas/sagas'
15 | import DevTools from './containers/DevTools'
16 | import { composeWithDevTools } from 'redux-devtools-extension'
17 |
18 |
19 |
20 | const sagaMiddleware = createSagaMiddleware()
21 |
22 | const reducer = combineReducers({
23 | counter,
24 | addInput,
25 | thunkReducer,
26 | sagaReducer
27 | })
28 | // redux-devtools 方式Ⅰ
29 | // const enhancer = compose(
30 | // // Middleware you want to use in development:
31 | // applyMiddleware(sagaMiddleware, thunk, logger),
32 | // // Required! Enable Redux DevTools with the monitors you chose
33 | // DevTools.instrument()
34 | // );
35 | // const store = createStore(reducer,enhancer )
36 |
37 | // redux-devtools-extension 方式Ⅱ
38 | const store = createStore(reducer, composeWithDevTools(
39 | applyMiddleware(sagaMiddleware, thunk, logger),
40 | // other store enhancers if any
41 | ));
42 |
43 | sagaMiddleware.run(rootSaga)
44 |
45 | export default store
--------------------------------------------------------------------------------