├── GeekTime-React-Course.pdf
├── README.md
├── demo.png
├── images
├── cow.png
├── elephant.png
└── tiger.png
├── package.json
├── public
└── index.html
└── src
├── Hello.js
├── c01
└── ChatApp.js
├── c02
├── CommentBox.css
├── CommentBox.js
├── CommentForm.js
├── CommentItem.js
├── CommentList.js
├── StatefulTabSelector.js
├── TabSelector.css
└── TabSelector.js
├── c03
└── Clock.js
├── c04
├── Clock.js
├── SnapshotSample.css
└── SnapshotSample.js
├── c05
└── DomDiff.js
├── c06
├── AdvancedTabSelector.js
└── withTimer.js
├── c07
└── LocaleSample.js
├── c11
└── PureRedux.js
├── c12
└── Counter.js
├── c13
├── AsyncAction.js
└── ReduxMiddleware.js
├── c14
└── OrgActions.js
├── c16
└── RouterSample.js
├── c17
├── NestedRoute.js
└── RouterParams.js
├── c29
├── FormBuilder.js
├── FormSubmit.js
└── FormSubmitAntd.js
├── c30
└── DynamicForm.js
├── c31
├── App.css
├── App.js
├── DetailPage.js
├── ListPage.js
├── actions.js
└── reducer.js
├── c33
└── MultipleRequest.js
├── c35
├── App.js
├── Step1.js
├── Step2.js
└── Step3.js
├── c36
├── Layout1.css
├── Layout1.js
├── Layout2.css
├── Layout2.js
├── LayoutResize.css
└── LayoutResize.js
├── c37
├── AntdDialog.js
├── PortalSample.css
└── PortalSample.js
├── c38
├── D3Sample.css
├── D3Sample.js
└── data.json
├── c40
├── DndSample.css
└── DndSample.js
├── c43
└── ReselectSample.js
├── c44
└── Suspense.js
├── index.css
├── index.js
└── index.less
/GeekTime-React-Course.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supnate/react-geek-time/e9cf2b61c95d75c86f29dc821576e3ccb22b0624/GeekTime-React-Course.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 极客时间:React 从入门到精通
2 | ### 所有课程已经更新完毕,感谢大家支持!🎉🎉🎉
3 |
4 | 本 Repo 包含了所有课程的演示代码,代码由 codesandbox.io 同步到本 Repo:https://codesandbox.io/s/6n20nrzlxz
5 |
6 | 通过 codesandbox.io 您可以在线修改演示代码尝试运行结果,无需本地部署。
7 |
8 | [课程讲稿下载](https://github.com/supnate/react-geek-time/raw/master/GeekTime-React-Course.pdf)
9 |
10 | # Demo 结构
11 | 如下图,左侧为所有代码,按课程顺序进行 Demo 的组织。右侧为演示界面,菜单显示了所有可运行的 Demo 链接。点击菜单可以看到运行的结果。
12 |
13 |
14 |
15 | # Demo 列表
16 | * 01 chat-app: 简单的 React 组件和交互演示
17 | * 02 comment-box:评论框界面的组件拆分和实现
18 | * 03 clock: JSX 语法演示
19 | * 04 clock: 显示当前时间的组件,演示生命周期方法的调用
20 | * 05 dom-diff:演示 DOM Diff 的算法原理
21 | * 06 adv-tab-selector,withTimer: 高阶组件和函数作为子组件
22 | * 07 locale-sample: 使用 Conext API 实现多语言切换
23 | * 11 pure-reducer:纯 Redux 的使用
24 | * 12 counter:在 React 中使用 Redux
25 | * 13 async-action:Redux 异步 action,中间件的概念
26 | * 14 org-action:如何组织 Redux 的 action 和 reducer
27 | * 16 router-sample:路由不只是页面切换,更是代码组织方式
28 | * 17.1 router-params:路由参数定义
29 | * 17.2 nexted-route:嵌套路由
30 | * 29.1 form-submit: 表单提交
31 | * 29.2 form-submit-antd:使用 antd 的表单组件
32 | * 30 dynamic-form:动态表单
33 | * 31 list-page:列表页的翻页,搜索和缓存
34 | * 33 multiple-request:页面多个请求的处理
35 | * 35 wizard-sample:基于路由实现向导页面
36 | * 36.1 layout1:常用布局的实现
37 | * 36.2 layout2: 常用布局的实现
38 | * 36.3 layout-resize:实现侧边栏可调整宽度
39 | * 37.1 portal-example:使用 React Portals 实现的对话框
40 | * 37.2 antd-dialog:使用 antd 实现对话框
41 | * 40 dnd-sample:在 React 中实现拖放功能
42 | * 43 reselect-sample:使用 reselect 避免重复计算
43 | * 44 suspense:React 的异步渲染
44 |
45 |
46 | # License
47 | MIT
48 |
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/supnate/react-geek-time/e9cf2b61c95d75c86f29dc821576e3ccb22b0624/demo.png
--------------------------------------------------------------------------------
/images/cow.png:
--------------------------------------------------------------------------------
1 | https://uploads.codesandbox.io/uploads/user/91e44e0f-7b6f-4393-ae82-5192a414ab1e/jJAv-cow.png
--------------------------------------------------------------------------------
/images/elephant.png:
--------------------------------------------------------------------------------
1 | https://uploads.codesandbox.io/uploads/user/91e44e0f-7b6f-4393-ae82-5192a414ab1e/nHjH-elephant.png
--------------------------------------------------------------------------------
/images/tiger.png:
--------------------------------------------------------------------------------
1 | https://uploads.codesandbox.io/uploads/user/91e44e0f-7b6f-4393-ae82-5192a414ab1e/wbwH-tiger.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-geek-time",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "antd": "3.7.1",
9 | "axios": "0.18.0",
10 | "d3": "5.5.0",
11 | "react": "16.4.1",
12 | "react-dom": "16.4.1",
13 | "react-redux": "5.0.7",
14 | "react-router": "4.3.1",
15 | "react-router-dom": "4.3.1",
16 | "react-scripts": "1.1.4",
17 | "redux": "4.0.0",
18 | "redux-logger": "3.0.6",
19 | "redux-thunk": "2.3.0",
20 | "reselect": "3.0.1",
21 | "simple-cache-provider": "0.7.0"
22 | },
23 | "devDependencies": {},
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 | }
31 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
28 | {this.renderButton()}
29 | {this.renderDialog()}
30 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/c37/PortalSample.css:
--------------------------------------------------------------------------------
1 | .portal-sample {
2 | position: absolute;
3 | padding: 20px;
4 | width: 500px;
5 | height: 300px;
6 | left: 50%;
7 | top: 30%;
8 | transform: translate(-50%, -50%);
9 | border-radius: 10px;
10 | border: 1px solid #ddd;
11 | box-shadow: 0px 0px 20px 2px #ddd;
12 | }
13 |
--------------------------------------------------------------------------------
/src/c37/PortalSample.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { Button } from "antd";
4 | import "./PortalSample.css";
5 |
6 | export default class PortalSample extends React.PureComponent {
7 | state = { visible: false };
8 | renderButton() {
9 | return (
10 |
126 |
127 |
128 |
129 |
(this.d3Node = node)} />
130 |
131 | );
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/c38/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "id1",
5 | "group": 1
6 | },
7 | {
8 | "id": "id2",
9 | "group": 2
10 | },
11 | {
12 | "id": "id3",
13 | "group": 3
14 | },
15 | {
16 | "id": "id4",
17 | "group": 4
18 | },
19 | {
20 | "id": "id5",
21 | "group": 5
22 | }
23 |
24 | ],
25 | "links": [
26 | {
27 | "source": "id1",
28 | "target": "id2",
29 | "value": 1
30 | },
31 | {
32 | "source": "id1",
33 | "target": "id3",
34 | "value": 1
35 | },
36 | {
37 | "source": "id1",
38 | "target": "id4",
39 | "value": 1
40 | },
41 | {
42 | "source": "id5",
43 | "target": "id4",
44 | "value": 1
45 | }
46 |
47 |
48 |
49 | ]
50 | }
--------------------------------------------------------------------------------
/src/c40/DndSample.css:
--------------------------------------------------------------------------------
1 | .dnd-sample ul {
2 | display: inline-block;
3 | margin: 0;
4 | padding: 0;
5 | background-color: #eee;
6 | }
7 |
8 | .dnd-sample li {
9 | cursor: default;
10 | list-style: none;
11 | border-bottom: 1px solid #ddd;
12 | padding: 10px;
13 | margin: 0;
14 | width: 300px;
15 | background-color: #fff;
16 | }
17 |
18 | .dnd-sample-mask {
19 | position: fixed;
20 | left: 0;
21 | right: 0;
22 | top: 0;
23 | bottom: 0;
24 | background: rgba(0, 0, 0, 0);
25 | }
26 |
--------------------------------------------------------------------------------
/src/c40/DndSample.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | require("./DndSample.css");
4 |
5 | const list = [];
6 | for (let i = 0; i < 10; i++) {
7 | list.push(`Item ${i + 1}`);
8 | }
9 |
10 | const move = (arr, startIndex, toIndex) => {
11 | arr = arr.slice();
12 | arr.splice(toIndex, 0, arr.splice(startIndex, 1)[0]);
13 | return arr;
14 | };
15 |
16 | const lineHeight = 42;
17 | class DndSample extends Component {
18 | constructor(props) {
19 | super(props);
20 | this.state.list = list;
21 | }
22 | state = {
23 | dragging: false,
24 | draggingIndex: -1,
25 | startPageY: 0,
26 | offsetPageY: 0,
27 | };
28 |
29 | handleMounseDown = (evt, index) => {
30 | this.setState({
31 | dragging: true,
32 | startPageY: evt.pageY,
33 | currentPageY: evt.pageY,
34 | draggingIndex: index,
35 | });
36 | };
37 | handleMouseUp = () => {
38 | this.setState({ dragging: false, startPageY: 0, draggingIndex: -1 });
39 | };
40 | handleMouseMove = evt => {
41 | let offset = evt.pageY - this.state.startPageY;
42 | const draggingIndex = this.state.draggingIndex;
43 | if (offset > lineHeight && draggingIndex < this.state.list.length - 1) {
44 | // move down
45 | offset -= lineHeight;
46 | this.setState({
47 | list: move(this.state.list, draggingIndex, draggingIndex + 1),
48 | draggingIndex: draggingIndex + 1,
49 | startPageY: this.state.startPageY + lineHeight,
50 | });
51 | } else if (offset < -lineHeight && draggingIndex > 0) {
52 | // move up
53 | offset += lineHeight;
54 | this.setState({
55 | list: move(this.state.list, draggingIndex, draggingIndex - 1),
56 | draggingIndex: draggingIndex - 1,
57 | startPageY: this.state.startPageY - lineHeight,
58 | });
59 | }
60 | this.setState({ offsetPageY: offset });
61 | };
62 |
63 | getDraggingStyle(index) {
64 | if (index !== this.state.draggingIndex) return {};
65 | return {
66 | backgroundColor: "#eee",
67 | transform: `translate(10px, ${this.state.offsetPageY}px)`,
68 | opacity: 0.5,
69 | };
70 | }
71 |
72 | render() {
73 | return (
74 |
75 |
76 | {this.state.list.map((text, i) => (
77 | - this.handleMounseDown(evt, i)}
80 | style={this.getDraggingStyle(i)}
81 | >
82 | {text}
83 |
84 | ))}
85 |
86 | {this.state.dragging && (
87 |
92 | )}
93 |
94 | );
95 | }
96 | }
97 |
98 | export default DndSample;
99 |
--------------------------------------------------------------------------------
/src/c43/ReselectSample.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button } from "antd";
3 | import { createSelector } from "reselect";
4 |
5 | function runDemo() {
6 | const shopItemsSelector = state => state.shop.items;
7 | const taxPercentSelector = state => state.shop.taxPercent;
8 |
9 | const subtotalSelector = createSelector(shopItemsSelector, items =>
10 | items.reduce((acc, item) => acc + item.value, 0),
11 | );
12 |
13 | const taxSelector = createSelector(
14 | subtotalSelector,
15 | taxPercentSelector,
16 | (subtotal, taxPercent) => subtotal * (taxPercent / 100),
17 | );
18 |
19 | const totalSelector = createSelector(
20 | subtotalSelector,
21 | taxSelector,
22 | (subtotal, tax) => ({ total: subtotal + tax }),
23 | );
24 |
25 | let exampleState = {
26 | shop: {
27 | taxPercent: 8,
28 | items: [{ name: "apple", value: 1.2 }, { name: "orange", value: 0.95 }],
29 | },
30 | };
31 |
32 | console.log(subtotalSelector(exampleState)); // 2.15
33 | console.log(taxSelector(exampleState)); // 0.172
34 | console.log(totalSelector(exampleState)); // { total: 2.322 }
35 | }
36 |
37 | export default class extends React.Component {
38 | render() {
39 | return (
40 |
41 |
42 |
43 |
44 |
* 打开控制台查看运行结果
45 |
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/c44/Suspense.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | const link = "https://codesandbox.io/s/8nq4w3jj39";
3 | export default () => (
4 |
5 | {link}
6 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | .menu-list {
2 | position: absolute;
3 | left: 0;
4 | top: 0;
5 | bottom: 0;
6 | width: 200px;
7 | list-style: none;
8 | background-color: #eee;
9 | padding: 0;
10 | margin: 0;
11 | padding-top: 30px;
12 | overflow: auto;
13 | }
14 |
15 | .menu-list li {
16 | padding: 5px 15px;
17 | color: #2175bc;
18 | }
19 |
20 | .menu-list li.is-active {
21 | background-color: #2175bc;
22 | color: #fff !important;
23 | }
24 |
25 | .link {
26 | display: block;
27 | cursor: pointer;
28 | }
29 |
30 | input {
31 | padding: 7px;
32 | outline: none;
33 | }
34 | button {
35 | color: #fff;
36 | background-color: #38f;
37 | padding: 8px 15px;
38 | border: none;
39 | cursor: pointer;
40 | outline: none;
41 | }
42 |
43 | .wizard-finish-step li {
44 | list-style: none;
45 | padding: 10px;
46 | }
47 | .wizard-finish-step label {
48 | font-weight: bold;
49 | display: inline-block;
50 | width: 100px;
51 | }
52 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render } from "react-dom";
3 | import Hello from "./Hello";
4 | import ChatApp from "./c01/ChatApp";
5 | import CommentBox from "./c02/CommentBox";
6 | import { TabSelectorSample } from "./c02/TabSelector";
7 | import { StatefulTabSelectSample } from "./c02/StatefulTabSelector";
8 | import Clock from "./c03/Clock";
9 | import SnapshotSample from "./c04/SnapshotSample";
10 | import DomDiff from "./c05/DomDiff";
11 | import { AdvancedTabSelectorSample } from "./c06/AdvancedTabSelector";
12 | import LocaleSample from "./c07/LocaleSample";
13 | import PureRedux from "./c11/PureRedux";
14 | import Counter from "./c12/Counter";
15 | import AsyncAction from "./c13/AsyncAction";
16 | import ReduxMiddleware from "./c13/ReduxMiddleware";
17 | import OrgActions from "./c14/OrgActions";
18 | import RouterSample from "./c16/RouterSample";
19 | import RouterParams from "./c17/RouterParams";
20 | import NestedRoute from "./c17/NestedRoute";
21 | import FormSubmit from "./c29/FormSubmit";
22 | import FormSubmitAntd from "./c29/FormSubmitAntd";
23 | import DynamicForm from "./c30/DynamicForm";
24 | import ListSample from "./c31/App";
25 | import WizardSample from "./c35/App";
26 | import Layout1 from "./c36/Layout1";
27 | import Layout2 from "./c36/Layout2";
28 | import LayoutResize from "./c36/LayoutResize";
29 | import PortalSample from "./c37/PortalSample";
30 | import AntdDialog from "./c37/AntdDialog";
31 | import D3Sample from "./c38/D3Sample";
32 | import DndSample from "./c40/DndSample";
33 | import ReselectSample from "./c43/ReselectSample";
34 | import Suspense from "./c44/Suspense";
35 | import "antd/dist/antd.css";
36 |
37 | import "./index.css";
38 |
39 | const styles = {
40 | fontFamily: "sans-serif",
41 | paddingLeft: "250px",
42 | };
43 |
44 | const routeMap = {
45 | chat: ChatApp,
46 | "comment-box": CommentBox,
47 | "tab-selector": TabSelectorSample,
48 | "stateful-tab-selector": StatefulTabSelectSample,
49 | "snapshot-sample": SnapshotSample,
50 | "dom-diff": DomDiff,
51 | "adv-tab-selector": AdvancedTabSelectorSample,
52 | "locale-sample": LocaleSample,
53 | clock: Clock,
54 | "pure-redux": PureRedux,
55 | counter: Counter,
56 | "async-action": AsyncAction,
57 | "redux-middleware": ReduxMiddleware,
58 | "org-actions": OrgActions,
59 | "router-sample": RouterSample,
60 | "router-params": RouterParams,
61 | "nested-route": NestedRoute,
62 | "form-submit": FormSubmit,
63 | "form-submit-antd": FormSubmitAntd,
64 | "dynamic-form": DynamicForm,
65 | "list-page": ListSample,
66 | "wizard-sample": WizardSample,
67 | layout1: Layout1,
68 | layout2: Layout2,
69 | "layout-resize": LayoutResize,
70 | "portal-sample": PortalSample,
71 | "antd-dialog": AntdDialog,
72 | "d3-sample": D3Sample,
73 | "dnd-sample": DndSample,
74 | "reselect-sample": ReselectSample,
75 | suspense: Suspense,
76 | };
77 |
78 | class App extends React.PureComponent {
79 | handleLinkClick = key => {
80 | // window.location.hash = `#${key}`;
81 | window.history.pushState(null, "", `/#/${key}`);
82 | this.forceUpdate();
83 | };
84 | render() {
85 | const currentPage = document.location.hash.replace(/#\/?/, "");
86 |
87 | let CurrentPage = routeMap[currentPage] || Hello;
88 | if (currentPage.match(/\/user\/\w+|\/list-page/)) {
89 | CurrentPage = ListSample;
90 | }
91 | if (currentPage.match(/\/wizard\/step\/\w+/)) {
92 | CurrentPage = WizardSample;
93 | }
94 | return (
95 |
96 |
97 | {Object.keys(routeMap).map(key => (
98 | -
103 | this.handleLinkClick(key)}>
104 | {key}
105 |
106 |
107 | ))}
108 |
109 |
110 |
111 |
112 |
113 | );
114 | }
115 | }
116 |
117 | render(
, document.getElementById("root"));
118 |
--------------------------------------------------------------------------------
/src/index.less:
--------------------------------------------------------------------------------
1 | body {
2 | color: red !important;
3 | }
4 | .link {
5 | color: red;
6 | }
7 |
--------------------------------------------------------------------------------