├── .gitignore
├── README.md
├── doc
├── React-Redux总结.md
├── React-Router总结.md
├── Reactjs总结.md
├── React中间件总结.md
├── Redux总结.md
└── hooks
│ ├── custorm hooks练习.md
│ └── 总结 hooks.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── React-Router
├── react-router-dom
│ ├── BrowserRouter.js
│ ├── HashRouter.js
│ ├── Link.js
│ ├── Redirect.js
│ ├── Route.js
│ └── Switch.js
└── 路由基础原理
│ ├── hash.html
│ └── history.html
├── Redux-React
└── components
│ └── connect.js
├── Redux
├── components
│ ├── 1.Redux-base.js
│ ├── 2.bindActionCreators-base.js
│ ├── 3.模块化reducer后的使用.js
│ └── 4.React-Redux.js
├── react-redux
│ ├── Provider.js
│ ├── connect.js
│ ├── context.js
│ └── index.js
├── redux
│ ├── applymiddleware.js
│ ├── bindActionCreators.js
│ ├── combineReducers.js
│ ├── compose.js
│ ├── createStore.js
│ └── index.js
├── store-模块化reducer
│ ├── action-types.js
│ ├── actions
│ │ ├── counter1.js
│ │ └── counter2.js
│ ├── index.js
│ └── reducers
│ │ ├── counter1.js
│ │ ├── counter2.js
│ │ └── index.js
└── 中间件函数
│ └── index.js
├── hooks
├── useCallback
│ └── index.jsx
├── useContext
│ └── index.js
├── useMemo
│ └── index.jsx
├── useReducer
│ └── index.js
└── useState
│ └── counter.jsx
├── index.js
└── my_react
├── index.js
├── react-dom
└── index.js
└── react
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 概述
2 |
3 | - 本仓库**并不是**一个**如何从零使用React**的仓库,只是我用来进行对React生态的一个: **总结 & 巩固 & flag**
4 |
5 | - 由于大部分都是自己学习后总结下来的代码,大多数描述都只在**代码中给出注释**
6 |
7 | - 代码中如果**有什么问题**,**一定要及时的告诉我,我谢谢您了**
8 |
9 | - 由于时间有限,可能大量的文字描述不会有太多。因此我会**尽量将笔记索引、代码文件结构 以及内嵌注释 的逻辑整理清晰再push**,尽量给您充足的思考空间以及减少不必要的麻烦
10 |
11 | ## 希望
12 |
13 | - 如果您也能熟练使用React及周边生态,但是对React的**进阶使用以及底层实现**不知道该如何下手以及重点,可以持续关注一下!
14 |
15 | - 如果您有收获,给个star支持一下小老弟!
16 |
17 | > 说明:由于我刚接触前端不久,所以目前**深度一定是有所欠缺**的,但一定会坚持下去的!
18 |
19 | ## 索引
20 |
21 | ### 全家桶基础
22 |
23 | - [x] [React.js 总结 (缓慢整理)](./doc/Reactjs总结.md)
24 | - [x] [React-Router 基础 & 原理](./doc/React-Router总结.md)
25 | - [x] [Redux 基础 & 原理](./doc/Redux总结.md)
26 | - [x] [React-Redux 基础 & 原理](./doc/React-Redux总结.md)
27 | - [x] [Redux 中间件 & 原理](./doc/React中间件总结.md)
28 |
29 | - [ ] [connected-react-router 基本使用]()
30 | - [ ] [connected-react-router 原理]()
31 | - [ ] [middleware - saga]()
32 |
33 | - [x] [react - hooks 学习笔记](./doc/hooks/总结%20hooks.md)
34 | - [ ] [custom - hooks 练习笔记(没有整理,混乱中)](./doc/hooks/custorm%20hooks练习.md)
35 |
36 | ### 状态管理
37 |
38 | - [ ] [frontendMaster : 状态管理最佳实践]()
39 |
40 | ### 周边生态
41 |
42 | - [ ] [dva]()
43 | - [ ] [roadhag]()
44 | - [ ] [umi]()
45 |
46 | #### antD 常用组件实现原理
47 |
--------------------------------------------------------------------------------
/doc/React-Redux总结.md:
--------------------------------------------------------------------------------
1 | ## Redux-React
2 |
3 | ### 动机
4 |
5 | 通常在组件使用原生Redux时需要做如下四件事儿:
6 |
7 | - 引入`store`
8 | - 将需要的状态映射成组件内部的状态 (`this.state = store.getState()`)
9 | - 当状态发生改变后,需要手动 订阅和取消订阅 一个“视图更新”函数,例如`this.setState()`
10 | - `bindActionCreators`使得组件内部方便执行`actions`
11 |
12 | 不同组件有如下几点区别:
13 |
14 | - 绑定的`actions`不一样(来自不同的`action文件`)
15 | - 映射的状态不同
16 |
17 | Redux-React意图将这一过程精简化,提高开发效率。
18 |
19 | 例如下面这个组件,使用[connect](./../src/Redux-React/components/connect.js) 函数简化了裸Redux的开发流程。
20 |
21 | > 当然,需要在app组件通过Provider组件将store向下传递
22 |
23 | ### 基本使用 & 实现
24 |
25 | [Provider组件](../src/Redux/react-redux/Provider.js)
26 |
27 | [connect高阶组件](../src/Redux/react-redux/connect.js)
28 |
--------------------------------------------------------------------------------
/doc/React-Router总结.md:
--------------------------------------------------------------------------------
1 | ## React-Router
2 |
3 | ### 使用层面
4 |
5 | #### 使用 - Routers 组件
6 |
7 | #### 使用 - Route 组件
8 |
9 | #### 使用 - Navigation 组件
10 |
11 | #### 使用 - 属性
12 |
13 | ##### history
14 |
15 | ##### location
16 |
17 | #### 使用 - hooks
18 |
19 | ### HashRouter & BrowserRouter 核心原理
20 |
21 | #### HashRouter路由实现原理
22 |
23 | - 监听 window.hashchange 事件
24 | - window.location 获取 当前URI信息
25 |
26 | ```html
27 |
28 | `注册`onClick`事件处理函数,触发时执行`history.push` 方法并将`this.props.to`传递进push中
165 |
166 | [Link](./../src/React-Router/react-router-dom/Link.js)
167 |
168 | #### Redirect
169 |
170 | - 渲染该组件时,在其内部`componentDidMount`生命周期中执行上层组件传递的`history.push()`方法跳转到`this.props.to`对应的url
171 |
--------------------------------------------------------------------------------
/doc/Reactjs总结.md:
--------------------------------------------------------------------------------
1 | # React.js
2 |
3 | ## React 和 vue
4 |
5 | react工作模式:UI=render(data);render函数是纯函数
6 |
7 | 学习曲线,代码风格,单文件组成,灵活性,工具,移动端支持,各自优缺点
8 |
9 | ## JSX
10 | ### createElement
11 | ### 不可变值
12 | ### this
13 | - bind(能够传递参数)
14 | - 匿名箭头函数(能够传递参数,但是没有bind语义强)
15 | - class 新语法
16 | ### for 循环key值问题
17 | ### 受控组件和非受控组件
18 | ### 合成事件系统
19 | - 抹平浏览器差异
20 | - 事件委托到document
21 | - NativeEvent
22 |
23 | ## 组件
24 | ### 状态流:setState
25 | #### 异步或同步执行
26 | **同步更新:**
27 | - “自定义”事件
28 | - 定时器
29 | #### 异步更新(批量更新)
30 |
31 | 事件处理函数
32 |
33 | #### 更新合并
34 | - 对象批量更新会出现覆盖现象
35 | - 函数式批量更新不会覆盖
36 | - VS useState hooks
37 | #### 不可变值
38 | - 直接修改值的后果(UI不刷新)
39 | - 原始值:覆盖
40 | - 引用值:重建
41 | - 数组处理
42 | - 对象处理
43 | ### 生命周期
44 |
45 | #### 老版本
46 | props 和 states 分的很清楚
47 | #### 新版本
48 | #### 为什么这样做
49 | todo:
50 | - 自己的笔记
51 | - 珠峰讲义,课堂源码先不看
52 | - react Fiber
53 |
54 | **父组件更新子组件也更新(好意,你不需要你自己控制呗)**
55 |
56 | ### 逻辑复用
57 | HOC高阶组件
58 | Render Props
59 | custorm Hooks
60 |
61 |
62 | ## 核心特性
63 | ### Suspense
64 | ### Portals
65 | ### ErrorBoudary
66 | ### React.lazy
67 | ### Fragment
68 | ### 异步加载组件
69 | #### import()抱: webPack代码分割
70 | #### 基本使用
71 | #### 异步错误处理
72 | #### - [ ] 配合React-router @官方文档
73 | ### context
74 | #### 解决问题:props传值嵌套,最佳实践(三层以上组件时)
75 | #### scu造成context短路
76 | #### - [ ] useContext VS createContext
77 | #### createContext VS contextTypes
78 | - [ ] contextTypes: 一对多
79 | - [ ] createContext:一对一
80 | @双越 + 官方文档
81 |
--------------------------------------------------------------------------------
/doc/React中间件总结.md:
--------------------------------------------------------------------------------
1 | ## redux中间件
2 |
3 | 原理:对dispatch进行改造,增加副作用(增加业务逻辑或异步请求)
4 |
5 | 
6 |
7 | ### 常见中间件
8 |
9 | **Logger:** 使用logger中间件后,在状态改变前后会执行状态打印。
10 |
11 | **Thunk:** 使用thunk中间件后,action可以是函数因此可以通过该函数执行一些额外逻辑后,再store再次派发一个action
12 | **Promise:** 可以让action 拥有then方法,在then方法中调用dispatch函数。
13 |
14 | - [ ] Saga:saga中间件能够将异步逻辑(复杂逻辑)统一管理到一个文件中
15 |
16 | ### 原理
17 |
18 | > src/Redux/中间件函数 + src/redux/ 中有写好的源码
19 |
20 | 以下为自己练习思路的代码:
21 |
22 | ```js
23 |
24 | // --- demo
25 |
26 | let store = createStore(reducer,{},applyMiddleware(promise,thunk,looger))
27 |
28 | export function createStore (reducer,initialState,enhancer) {
29 | // ...
30 | if(typeof enhancer !== 'undefined') {
31 | return enhancer(createStore)(reducer,initialState)
32 | }
33 | // ...
34 | }
35 |
36 | /**
37 | * 思路:
38 | * - 创建 store 并返回
39 | * - 重写dispatch 方法
40 | * @param {...any} middlewares
41 | */
42 | export const applyMiddleware = (...middlewares) => createStore => (reducer,initialState) => {
43 | let store = createStore(reducer,initialState);
44 |
45 | let dispatch = undefined;
46 | let middlewareAPI = {
47 | dispatch: (action) => dispatch(action),
48 | getState: store.getState
49 | }
50 | // 处理中间件,将API缓存
51 |
52 | middlewares = middlewares.map(m => m(middlewareAPI))
53 |
54 | // 加持dispatch方法
55 | // 需要:(...args) => a(b(c(args)))
56 | dispatch = compose(...middlewares)(sotre.dispatch)
57 |
58 | return {
59 | ...store,
60 | dispatch
61 | }
62 | }
63 |
64 | // 避免一个中间件进来,数组收一下
65 | let compose = (...middlewares) => middlewares.reducer((a,b) => (...args) => a(b(args)))
66 | ```
67 |
--------------------------------------------------------------------------------
/doc/Redux总结.md:
--------------------------------------------------------------------------------
1 | ## Redux总结
2 |
3 | ### Redux 哲学
4 |
5 | **唯一数据源**: 全局维持一个store
6 |
7 | **不可变性**: 改变状态通过派发action,不能直接修改store中的值
8 |
9 | **纯函数reducer** : 接受action,根据老状态和新action生成新的状态返还给Store
10 |
11 | ### 使用Redux的好处
12 |
13 | 1. **视图层变得很薄**,只包含渲染逻辑和触发action这两个职责
14 | 2. **状态变更逻辑清晰**
15 | 3. 强制让状态变更时留下一笔记录也就是dispatcher,利用它去做**debug工具**,**历史回滚等工具**
16 |
17 | ### reducer
18 |
19 | 之所以叫 `reducer` 是因为他和 `Array.prototype.reduce` 函数非常相似,本质上都是根据前一个状态返回一个新的状态。
20 |
21 | **例如reducer 根据 prevState + action => newState**
22 |
23 | 同时reducer 也有缩减的含义,现在有两个状态:前一刻的状态和想要变更的状态,当reducer函数执行后,只剩一个新的状态,不就是缩减的意思吗?
24 |
25 | ### 工作流
26 |
27 | 
28 |
29 | > 注意维持reducer 纯函数特性(不产生任何副作用,也不能修改参数state和action对象)
30 |
31 | ### 基本使用 & 实现
32 |
33 | #### 基本工作流使用
34 |
35 | [Redux-base](../../src/Redux/components/1.Redux-base.js)
36 |
37 | **核心方法实现:**
38 |
39 | - 初始化Store
40 | - `getState()`
41 | - `dispatch()`
42 | - `subscribe()`
43 |
44 | [redux store模拟实现](../../src/Redux/redux/createStore.js)
45 |
46 | #### bindActionCreators基本使用
47 |
48 | [bindActionCreators-base](./../../src/Redux/components/2.bindActionCreators-base.js)
49 |
50 | **方法实现:**
51 | [bindActionCreators-实现](./../../src/Redux/redux/bindActionCreators.js)
52 |
53 | #### 模块化拆分reducers
54 |
55 | 主要是使用`combineReducers`,[入口文件](./../../src/Redux/store-模块化reducer/reducers/index.js)
56 |
57 | **方法实现:**
58 | [combineReducers](./../../src/Redux/redux/combineReducers.js)
59 |
--------------------------------------------------------------------------------
/doc/hooks/custorm hooks练习.md:
--------------------------------------------------------------------------------
1 |
2 | ## 9.2 中间件 [#](#t279.2 中间件)
3 |
4 | #### 9.2.1 logger [#](#t289.2.1 logger)
5 |
6 | ```
7 | import React, { useEffect, useState, useReducer } from 'react';
8 | import ReactDOM from 'react-dom';
9 | const initialState = 0;
10 |
11 | function reducer(state, action) {
12 | switch (action.type) {
13 | case 'increment':
14 | return { number: state.number + 1 };
15 | case 'decrement':
16 | return { number: state.number - 1 };
17 | default:
18 | throw new Error();
19 | }
20 | }
21 | function init(initialState) {
22 | return { number: initialState };
23 | }
24 | function useLogger(reducer, initialState, init) {
25 | const [state, dispatch] = useReducer(reducer, initialState, init);
26 | let dispatchWithLogger = (action) => {
27 | console.log('老状态', state);
28 | dispatch(action);
29 | }
30 | useEffect(function () {
31 | console.log('新状态', state);
32 | }, [state]);
33 | return [state, dispatchWithLogger];
34 | }
35 | function Counter() {
36 | const [state, dispatch] = useLogger(reducer, initialState, init);
37 | return (
38 | <>
39 | Count: {state.number}
40 |
41 |
42 | >
43 | )
44 | }
45 | ReactDOM.render(, document.getElementById('root'));
46 | ```
47 |
48 | #### 9.2.2 promise [#](#t299.2.2 promise)
49 |
50 | ```
51 | import React, { useEffect, useState, useReducer } from 'react';
52 | import ReactDOM from 'react-dom';
53 | const initialState = 0;
54 |
55 | function reducer(state, action) {
56 | switch (action.type) {
57 | case 'increment':
58 | return { number: state.number + 1 };
59 | case 'decrement':
60 | return { number: state.number - 1 };
61 | default:
62 | throw new Error();
63 | }
64 | }
65 | function init(initialState) {
66 | return { number: initialState };
67 | }
68 | function useLogger(reducer, initialState, init) {
69 | const [state, dispatch] = useReducer(reducer, initialState, init);
70 | let dispatchWithLogger = (action) => {
71 | console.log('老状态', state);
72 | dispatch(action);
73 | }
74 | useEffect(function () {
75 | console.log('新状态', state);
76 | }, [state]);
77 | return [state, dispatchWithLogger];
78 | }
79 | function usePromise(reducer, initialState, init) {
80 | const [state, dispatch] = useReducer(reducer, initialState, init);
81 | let dispatchPromise = (action) => {
82 | if (action.payload && action.payload.then) {
83 | action.payload.then((payload) => dispatch({ ...action, payload }));
84 | } else {
85 | dispatch(action);
86 | }
87 | }
88 | return [state, dispatchPromise];
89 | }
90 | function Counter() {
91 | const [state, dispatch] = usePromise(reducer, initialState, init);
92 | return (
93 | <>
94 | Count: {state.number}
95 |
96 |
102 | >
103 | )
104 | }
105 | ReactDOM.render(, document.getElementById('root'));
106 | ```
107 |
108 | #### 9.2.3 thunk [#](#t309.2.3 thunk)
109 |
110 | ```
111 | import React, { useEffect, useState, useReducer } from 'react';
112 | import ReactDOM from 'react-dom';
113 | import { resolve } from 'dns';
114 | const initialState = 0;
115 |
116 | function reducer(state, action) {
117 | switch (action.type) {
118 | case 'increment':
119 | return { number: state.number + 1 };
120 | case 'decrement':
121 | return { number: state.number - 1 };
122 | default:
123 | throw new Error();
124 | }
125 | }
126 | function init(initialState) {
127 | return { number: initialState };
128 | }
129 | function useLogger(reducer, initialState, init) {
130 | const [state, dispatch] = useReducer(reducer, initialState, init);
131 | let dispatchWithLogger = (action) => {
132 | console.log('老状态', state);
133 | dispatch(action);
134 | }
135 | useEffect(function () {
136 | console.log('新状态', state);
137 | }, [state]);
138 | return [state, dispatchWithLogger];
139 | }
140 | function usePromise(reducer, initialState, init) {
141 | const [state, dispatch] = useReducer(reducer, initialState, init);
142 | let dispatchPromise = (action) => {
143 | if (action.payload && action.payload.then) {
144 | action.payload.then((payload) => dispatch({ ...action, payload }));
145 | } else {
146 | dispatch(action);
147 | }
148 | }
149 | return [state, dispatchPromise];
150 | }
151 | function useThunk(reducer, initialState, init) {
152 | const [state, dispatch] = useReducer(reducer, initialState, init);
153 | let dispatchPromise = (action) => {
154 | if (typeof action === 'function') {
155 | action(dispatchPromise, () => state);
156 | } else {
157 | dispatch(action)
158 | }
159 | }
160 | return [state, dispatchPromise];
161 | }
162 | function Counter() {
163 | const [state, dispatch] = useThunk(reducer, initialState, init);
164 | return (
165 | <>
166 | Count: {state.number}
167 |
168 |
173 | >
174 | )
175 | }
176 | ReactDOM.render(, document.getElementById('root'));
177 | ```
178 |
179 | ### 9.3 ajax [#](#t319.3 ajax)
180 |
181 | ```
182 | import React, { useState, useEffect, useLayoutEffect } from 'react';
183 | import ReactDOM from 'react-dom';
184 | function useRequest(url) {
185 | let limit = 5;
186 | let [offset, setOffset] = useState(0);
187 | let [data, setData] = useState([]);
188 | function loadMore() {
189 | setData(null);
190 | fetch(`${url}?offset=${offset}&limit=${limit}`)
191 | .then(response => response.json())
192 | .then(pageData => {
193 | setData([...data, ...pageData]);
194 | setOffset(offset + pageData.length);
195 | });
196 | }
197 | useEffect(loadMore, []);
198 | return [data, loadMore];
199 | }
200 |
201 | function App() {
202 | const [users, loadMore] = useRequest('http://localhost:8000/api/users');
203 | if (users === null) {
204 | return 正在加载中....
205 | }
206 | return (
207 | <>
208 |
209 | {
210 | users.map((item, index) => - {item.id}:{item.name}
)
211 | }
212 |
213 |
214 | >
215 | )
216 | }
217 | ReactDOM.render(, document.getElementById('root'));
218 | ```
219 |
220 | async+await
221 |
222 | ```
223 | import React, { useState, useEffect, useLayoutEffect } from 'react';
224 | import ReactDOM from 'react-dom';
225 | function useRequest(url) {
226 | let limit = 5;
227 | let [offset, setOffset] = useState(0);
228 | let [data, setData] = useState([]);
229 | async function loadMore() {
230 | setData(null);
231 | let pageData = await fetch(`${url}?offset=${offset}&limit=${limit}`)
232 | .then(response => response.json());
233 | setData([...data, ...pageData]);
234 | setOffset(offset + pageData.length);
235 | }
236 | useEffect(loadMore, []);
237 | return [data, loadMore];
238 | }
239 |
240 | function App() {
241 | const [users, loadMore] = useRequest('http://localhost:8000/api/users');
242 | if (users === null) {
243 | return 正在加载中....
244 | }
245 | return (
246 | <>
247 |
248 | {
249 | users.map((item, index) => - {item.id}:{item.name}
)
250 | }
251 |
252 |
253 | >
254 | )
255 | }
256 | ReactDOM.render(, document.getElementById('root'));
257 | ```
258 |
259 | ```
260 | let express = require('express');
261 | let app = express();
262 | app.use(function (req, res, next) {
263 | res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
264 | next();
265 | });
266 | app.get('/api/users', function (req, res) {
267 | let offset = parseInt(req.query.offset);
268 | let limit = parseInt(req.query.limit);
269 | let result = [];
270 | for (let i = offset; i < offset + limit; i++) {
271 | result.push({ id: i + 1, name: 'name' + (i + 1) });
272 | }
273 | res.json(result);
274 | });
275 | app.listen(8000);
276 | ```
277 |
278 | ### 9.4 动画 [#](#t329.4 动画)
279 |
280 | ```
281 | import React, { useState, useEffect, useLayoutEffect } from 'react';
282 | import ReactDOM from 'react-dom';
283 | import './index.css';
284 | function useMove(initialClassName) {
285 | const [className, setClassName] = useState(initialClassName);
286 | const [state, setState] = useState('');
287 | function start() {
288 | setState('bigger');
289 | }
290 | useEffect(() => {
291 | if (state === 'bigger') {
292 | setClassName(`${initialClassName} ${initialClassName}-bigger`);
293 | }
294 | }, [state]);
295 | return [className, start];
296 | }
297 |
298 | function App() {
299 | const [className, start] = useMove('circle');
300 | return (
301 |
305 | )
306 | }
307 | ReactDOM.render(, document.getElementById('root'));
308 | ```
309 |
310 | ```
311 | .circle {
312 | width : 50px;
313 | height : 50px;
314 | border-radius: 50%;
315 | background : red;
316 | transition: all .5s;
317 | }
318 | .circle-bigger {
319 | width : 200px;
320 | height : 200px;
321 | }
322 | ```
323 |
324 | 10. 路由 hooks [#](#t3310.路由hooks)
325 |
326 | --------------------------------
327 |
328 | ### 10.1 useParams [#](#t3410.1 useParams)
329 |
330 | * 获取路由中的 params
331 |
332 | #### 10.1.1 老版 [#](#t3510.1.1 老版)
333 |
334 | ```
335 | import React from "react";
336 | import ReactDOM from "react-dom";
337 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
338 |
339 | function Post({ match }) {
340 | let { title } = match.params;
341 | return {title}
;
342 | }
343 |
344 | ReactDOM.render(
345 |
346 |
347 |
348 |
349 |
350 |
351 | ,
352 | document.getElementById("root")
353 | );
354 | ```
355 |
356 | #### 10.1.2. 新版 [#](#t3610.1.2.新版)
357 |
358 | ```
359 | import React from "react";
360 | import ReactDOM from "react-dom";
361 | +import { BrowserRouter as Router, Route, Switch, useParams } from "react-router-dom";
362 |
363 | +function Post() {
364 | + let { title } = useParams();
365 | + return {title}
;
366 | +}
367 |
368 | ReactDOM.render(
369 |
370 |
371 |
372 | +
373 |
374 |
375 | ,
376 | document.getElementById("root")
377 | );
378 | ```
379 |
380 | ### 10.2.useLocation [#](#t3710.2.useLocation)
381 |
382 | * 可以查看当前路由
383 |
384 | #### 10.2.1. 老版 [#](#t3810.2.1.老版)
385 |
386 | ```
387 | import React from "react";
388 | import ReactDOM from "react-dom";
389 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
390 |
391 | function Post({ match, location }) {
392 | let { title } = match.params;
393 | return {title}{JSON.stringify(location)}
;
394 | }
395 |
396 | ReactDOM.render(
397 |
398 |
399 |
400 |
401 |
402 |
403 | ,
404 | document.getElementById("root")
405 | );
406 | ```
407 |
408 | #### 10.2.2 新版 [#](#t3910.2.2 新版)
409 |
410 | ```
411 | import React from "react";
412 | import ReactDOM from "react-dom";
413 | import { BrowserRouter as Router, Route, Switch, useParams, useLocation } from "react-router-dom";
414 |
415 | function Post() {
416 | let { title } = useParams();
417 | + const location = useLocation();
418 | + return {title}
{JSON.stringify(location)};
419 | }
420 |
421 | ReactDOM.render(
422 |
423 |
428 | ,
429 | document.getElementById("root")
430 | );
431 | ```
432 |
433 | ### 10.3.useHistory [#](#t4010.3.useHistory)
434 |
435 | * 可以返回上一个网页
436 |
437 | #### 10.3.1 老版 [#](#t4110.3.1 老版)
438 |
439 | ```
440 | import React from "react";
441 | import ReactDOM from "react-dom";
442 | import { BrowserRouter as Router, Route, Switch, useHistory } from "react-router-dom";
443 |
444 | function Post({ match, history }) {
445 | let { title } = match.params;
446 | return (
447 |
448 | {title}
449 |
450 |
453 |
454 | );
455 | }
456 | function Home({ history }) {
457 | return (
458 | <>
459 |
462 | >
463 | )
464 | }
465 | ReactDOM.render(
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 | ,
474 | document.getElementById("root")
475 | );
476 | ```
477 |
478 | #### 10.3.2 新版 [#](#t4210.3.2 新版)
479 |
480 | ```
481 | import React from "react";
482 | import ReactDOM from "react-dom";
483 | +import { BrowserRouter as Router, Route, Switch, useParams, useHistory } from "react-router-dom";
484 |
485 | function Post() {
486 | + let { title } = useParams();
487 | + let history = useHistory();
488 | return (
489 |
490 | {title}
491 |
492 | +
495 |
496 | );
497 | }
498 | function Home() {
499 | + let history = useHistory();
500 | return (
501 | <>
502 | +
505 | >
506 | )
507 | }
508 | ReactDOM.render(
509 |
510 |
511 |
512 | +
513 | +
514 |
515 |
516 | ,
517 | document.getElementById("root")
518 | );
519 | ```
520 |
521 | ### 10.4 useRouteMatch [#](#t4310.4 useRouteMatch)
522 |
523 | * `useRouteMatch`挂钩尝试以与`Route`相同的方式匹配当前`URL`
524 | * 在无需实际呈现`Route`的情况下访问匹配数据最有用
525 |
526 | #### 10.4.1 旧版 [#](#t4410.4.1 旧版)
527 |
528 | ```
529 | import React from 'react';
530 | import ReactDOM from 'react-dom';
531 | import { BrowserRouter as Router, Route } from 'react-router-dom';
532 | function NotFound() {
533 | return Not Found
534 | }
535 | function Post(props) {
536 | return (
537 | {props.match.params.title}
538 | )
539 | }
540 | function App() {
541 | return (
542 |
543 |
match ? : }
548 | />
549 |
550 | )
551 | }
552 |
553 | ReactDOM.render(
554 |
555 |
556 | ,
557 | document.getElementById("root")
558 | );
559 | ```
560 |
561 | #### 10.4.2 新版 [#](#t4510.4.2 新版)
562 |
563 | ```
564 | import React from 'react';
565 | import ReactDOM from 'react-dom';
566 | import { BrowserRouter as Router, Route, useRouteMatch } from 'react-router-dom';
567 | function NotFound() {
568 | return Not Found
569 | }
570 | function Post(props) {
571 | return (
572 | {props.match.params.title}
573 | )
574 | }
575 | function App() {
576 | let match = useRouteMatch({
577 | path: '/post/:title',
578 | strict: true,
579 | sensitive: true
580 | })
581 | console.log(match);
582 | return (
583 |
586 | )
587 | }
588 |
589 | ReactDOM.render(
590 |
591 |
592 | ,
593 | document.getElementById("root")
594 | );
595 | ```
596 |
--------------------------------------------------------------------------------
/doc/hooks/总结 hooks.md:
--------------------------------------------------------------------------------
1 | # React Hooks
2 |
3 | ## 概念
4 |
5 | ### 是什么
6 |
7 | Hook 是 React 16.8 的新增特性,可以让你在不编写 class 的情况下**使用 state 以及其他的 React 特性**。
8 |
9 | - 拥有类似class组件(state)的响应式数据(useState)
10 |
11 | - 拥有控制组件生命周期的能力(useEffect),当然useEffect 远不如此
12 | - [ ] Ref
13 | - 拥有类似Redux对复杂状态的管理能力(useReducer)
14 |
15 | ### 解决的问题
16 |
17 | * 在组件之间复用状态逻辑很难, 可能要用到 render props 和高阶组件,React 需要为共享状态逻辑提供更好的原生途径,Hook 使你在无需修改组件结构的情况下**复用状态逻辑**
18 |
19 | * 复杂组件变得难以理解,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
20 |
21 | * 难以捉摸的`this`
22 |
23 | - [ ] 性能提升
24 |
25 | ### 注意事项
26 |
27 | * 只能在函数最外层调用 Hook,不要在循环、条件判断或者子函数中调用。
28 | * 只能在 React函数组件中调用 Hook。
29 |
30 | ## 心得总结
31 |
32 | ### useState
33 |
34 | #### 能力层面
35 |
36 | * 通过在函数组件里调用它来给组件添加一些内部的state,当组件重复渲染时会**状态复用**(而不是新创建一个状态)
37 | * 更改状态的函数类似 class 组件的 this.setState,但是并**不会**进行**状态合并**
38 |
39 | #### 使用层面
40 |
41 | * useState 的参数不能为`null`
42 | * 返回值:
43 | * 在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同
44 | * setState 函数用于更新 state。它接收一个**新的 state 值**,当他**执行后会将组件的一次重新渲染推入到渲染队列**
45 |
46 | ```react
47 | const [state, setState] = useState(initialState);
48 | ```
49 |
50 | #### demo:计数器
51 |
52 | ```react
53 | import React, { useState } from "react";
54 | export class CounterClass extends React.Component {
55 | constructor(props) {
56 | super(props);
57 | this.state = {
58 | number: 0,
59 | };
60 | }
61 | handleClick = () => {
62 | this.setState({ number: this.state.number + 1 });
63 | };
64 | render() {
65 | return (
66 |
67 |
{this.state.number}
68 |
69 |
70 | );
71 | }
72 | }
73 | export const Counter = () => {
74 | const [counter, setCounter] = useState(0);
75 | return (
76 |
77 |
{counter}
78 | {/* NOTE:
79 |
80 | setState函数在使用时候,要注意避免循环渲染,例如这样调用:
81 |
94 | );
95 | };
96 |
97 | ```
98 |
99 | #### ‼️Trick
100 |
101 | **1. 闭包现象:**
102 |
103 | ```react
104 | export const CounterClosure = () => {
105 | let [state, setState] = useState(0);
106 | const alertNumber = () => {
107 | console.log(`点击alert后,此时作用域下的state为:`, state);
108 | setTimeout(() => {
109 | alert(state);
110 | }, 3000);
111 | };
112 | const handleClick = () => {
113 | console.log(`组件重新渲染, 新的作用域下的state为:`, state);
114 | setState((lastState) => lastState + 1);
115 | };
116 | return (
117 |
118 |
当前state中的number为:{state}
119 |
点击我创建新的作用域并将状态加1
120 |
点击我锁定当前作用域并准备alert
121 |
122 | );
123 | };
124 | ```
125 |
126 | * 重新渲染组件会创建一个新的函数作用域(函数组件就是一个函数)
127 | * alert 会 “锁定” 点击事件触发时的作用域内的状态(闭包现象)
128 | * 视图上的状态虽然已经刷新,但是当alert触发时,依旧使用的是“老作用域”
129 |
130 | > [making-setinterval-declarative-with-react-hooks](https://overreacted.io/making-setinterval-declarative-with-react-hooks/)
131 |
132 | **2. 函数式更新:**
133 |
134 | ```react
135 | export const CounterUpdateFunctional = () => {
136 | let [state, setState] = useState({ number: 0 });
137 | // 无法跟进当前页面最新值,会将当前页面的值修改到触发时锁定的作用域内的值
138 | function lazy() {
139 | setTimeout(() => {
140 | setState({ number: state.number + 1 });
141 | }, 1000);
142 | }
143 | // 能够跟进当前组件的状态,保证累加到当前渲染的组件身上
144 | function lazyFunction() {
145 | setTimeout(() => {
146 | setState((state) => ({ number: state.number + 1 }));
147 | }, 3000);
148 | }
149 | return (
150 |
151 |
{state.number}
152 |
153 |
setState({ number: state.number + 1 })}>
154 | 点我正常增加
155 |
156 |
点我会锁定当前增加的值
157 |
点我会在正常增加基础上再增加
158 |
159 | );
160 | };
161 | ```
162 |
163 | * 如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。
164 | * 该函数将接收先前的 state,并返回一个更新后的值。
165 | * 使用函数式更新能够避免闭包导致的副作用
166 |
167 | **3. 惰性初始化initialState & 不合并更新状态:**
168 |
169 | ```react
170 |
171 | export const CounterLazyNoMergeState = () => {
172 | let [state, setState] = useState(function () {
173 | console.log("只有在首次渲染组件时会被执行");
174 | return { number: 0, name: "计数器" };
175 | });
176 | //1.initialState初始状态参数只会有组件初始渲染的时候调用,后续更新渲染时会被忽略
177 |
178 | //2.跟类组件setState不同,这里的状态不会自动合并 ,更新的时候要传入完整的值
179 |
180 | // eg:点击button1时此时name会消失
181 |
182 | return (
183 |
184 |
185 | {state.name}:{state.number}
186 |
187 |
setState({ number: state.number + 1 })}>
188 | 我更新后不会合并参数
189 |
190 |
setState({ ...state, number: state.number + 1 })}>
191 | 那就手动合并参数喽
192 |
193 |
194 | );
195 | };
196 | ```
197 |
198 | * initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略
199 | * 如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用
200 | * 与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象。你可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果
201 |
202 | #### ‼️ 性能优化方案
203 |
204 | **1. Object.is:(内置)**
205 |
206 | ```react
207 | export const performanceCounter = () => {
208 | let [state, setState] = useState(function () {
209 | return { number: 0, name: "计数器" };
210 | });
211 | return (
212 |
213 |
214 | {state.name}:{state.number}
215 |
216 |
setState({ ...state, number: state.number + 1 })}>
217 | 不能进行深度比较
218 |
219 | {/* 此时会判定为相同对象,不会进行更新操作,使用的是Object.is方法来判断是否相同 */}
220 | {/* https://developer.mozilla.org/zhCN/docs/Web/JavaScript/Reference/Global_Objects/Object/is
221 | */}
222 |
setState(state)}>只能进行浅层比较
223 |
224 | );
225 | };
226 | ```
227 |
228 | - setState会通过该比较算法来判断新旧值,以达到避免重复渲染的目的。
229 |
230 | **2. 缓存函数,减少重复渲染:**
231 |
232 | ```react
233 | let prevIncrease;
234 | let prevChangeName;
235 | export const performanceCacheCounter = () => {
236 | let [number, setNumber] = useState(0);
237 | let [name, setName] = useState("su");
238 | // const increase = () => setNumber(number + 1) // 不能被缓存的函数
239 | const increase = useCallback(() => setNumber(number + 1), [number]); // 能够被缓存的函数
240 | /**
241 | * 如果不用useCallback 每次的函数都不相同;
242 | *
243 | * 使用 useCallback 后,就能保证在依赖项不改变的情况下使用缓存的函数;
244 | *
245 | * */
246 |
247 | console.log("组件更新后,increase是否进行缓存 ? ", prevIncrease === increase);
248 | prevIncrease = increase;
249 | const changeName = useCallback(() => setName(Date.now()), [name]);
250 | console.log(
251 | "组件更新后,increase是否进行缓存 ? ",prevChangeName === changeName);
252 | prevChangeName = changeName;
253 | return (
254 |
255 |
256 | {name}:{number}
257 |
258 |
259 | 点我只更新number,按道理setName应该被缓存,不会重新创建
260 |
261 |
262 | 点我只更新name,按道理setNumber应该被缓存,不会重新创建
263 |
264 |
265 | );
266 | };
267 | ```
268 |
269 | * 把内联回调函数及依赖项作为参数传入 `useCallback`,它将返回memoized 版本的回调函数,仅在依赖项改变后才会重新创建
270 | * 这种优化有助于避免在每次渲染时都进行高开销的计算
271 |
272 | > useMemo同样可以实现,useCallback只是 他的 一个 语法糖?
273 |
274 | ### useMemo & useCallback
275 |
276 | #### 能力层面
277 |
278 | `useCallback` gives you [**referential equality**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness) **between renders** for **functions**. And `useMemo` gives you **referential equality between renders** for **values**.
279 |
280 | #### 使用层面
281 |
282 | - **useMemo: *Returns a memoized value.***
283 |
284 | - **useCallback:*Returns a memoized callback.***
285 |
286 | `useCallback` and `useMemo` both expect a function and an array of dependencies. The difference is that `useCallback` returns its function when the dependencies change while `useMemo` calls its function and returns the result.
287 |
288 | #### demo: 解释何时需要缓存
289 |
290 | ```react
291 | // 注意:Child 接收了 addClick 函数作为参数
292 | // 如果没有对该函数做缓存,会导致 addClick 重复创建新的内存对象。
293 |
294 | let Child = ({ data, addClick }) => {
295 | console.log("Child组件渲染了");
296 | return {data.number};
297 | };
298 |
299 |
300 | // memo:让函数组件拥有了记忆的功能,只有当组件内部状态发生变更的时候才会重新渲染。
301 | // 但是,由于addClick 是一个函数,如果不做缓存处理,每次都是一个新的值,会导致Child组件渲染。
302 |
303 | Child = memo(Child);
304 |
305 | let lastAddClick,lastData;
306 |
307 | export const UseMemoPerformance = () => {
308 | let [number, setNumber] = useState(0);
309 | let [name, setName] = useState("");
310 |
311 | //第一个参数deps,表示此函数缓存依赖的项,依赖改变后才会创建新的函数。
312 | const addClick = useCallback(() => setNumber(number + 1), [number]);
313 |
314 | console.log("lastAddClick === addClick", lastAddClick === addClick);
315 |
316 | lastAddClick = addClick;
317 | // 比useCallback厉害之处在于能够缓存函数的返回值,其解决的问题是相同的(缓存)
318 | const data = useMemo(() => ({ number }), [number]);
319 | console.log("lastData === data", lastData === data);
320 | lastData = data;
321 | return (
322 |
323 | setName(e.target.value)}
327 | />
328 |
329 |
330 | );
331 | };
332 | ```
333 |
334 | #### ‼️useMemo vs useCallback
335 |
336 | So what is the difference? `useCallback` **returns its function uncalled** so you can call it later, while `useMemo` **calls its function and returns the result**.
337 |
338 | ```react
339 | function foo() {
340 | return 'bar';
341 | }
342 |
343 | const memoizedCallback = useCallback(foo, []);
344 | const memoizedResult = useMemo(foo, []);
345 |
346 | memoizedCallback;
347 | // ƒ foo() {
348 | // return 'bar';
349 | // }
350 | memoizedResult; // 'bar'
351 | memoizedCallback(); // 'bar'
352 | memoizedResult(); // 🔴 TypeError
353 | ```
354 |
355 |
356 |
357 | ### useReducer
358 |
359 | #### 能力层面
360 |
361 | * 在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子数据(复杂对象),或者下一个 state 依赖于之前的 state 等
362 | * 为函数组件提供类似Redux的状态流管理的能力
363 |
364 | #### 使用层面
365 |
366 | - 它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
367 |
368 | ```
369 | const [state, dispatch] = useReducer(reducer, initialArg, init);
370 | ```
371 |
372 | #### demo: 用法
373 |
374 | ```
375 | const initialState = 0;
376 |
377 | function reducer(state, action) {
378 | switch (action.type) {
379 | case 'increment':
380 | return {number: state.number + 1};
381 | case 'decrement':
382 | return {number: state.number - 1};
383 | default:
384 | throw new Error();
385 | }
386 | }
387 | function init(initialState){
388 | return {number:initialState};
389 | }
390 | function Counter(){
391 | const [state, dispatch] = useReducer(reducer, initialState,init);
392 | return (
393 | <>
394 | Count: {state.number}
395 | dispatch({type: 'increment'})}>+
396 | dispatch({type: 'decrement'})}>-
397 | >
398 | )
399 | }
400 | ```
401 |
402 | #### ‼️最佳实践
403 |
404 | - [ ] fm
405 |
406 | ### useContext
407 |
408 | #### 能力层面(语法糖?)
409 |
410 | * useContext(MyContext) 相当于 class 组件中的 ``
411 | * useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化,你仍然需要在上层组件树中使用 来为下层组件提供 context
412 |
413 | #### 使用层面
414 |
415 | - 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值
416 |
417 | * 当前的 context 值由上层组件中距离当前组件最近的 决定
418 | * 当组件上层最近的 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值
419 |
420 | > 当组件上层最近的 `` 更新时,该 Hook 会触发重渲染,并使用最新传递给 `MyContext` provider 的 context `value` 值。即使祖先使用 [React.memo](https://zh-hans.reactjs.org/docs/react-api.html#reactmemo) 或 [shouldComponentUpdate](https://zh-hans.reactjs.org/docs/react-component.html#shouldcomponentupdate),也会在组件本身使用 `useContext` 时重新渲染。
421 |
422 | #### demo
423 |
424 | ```react
425 | function Counter() {
426 | // 只是把MyContext解构出来,不要想多了
427 | // 不管父组件是否进行SCU优化,或者memo优化,只要MyContext发生改变,就会重新渲染该组件
428 | let { state, setState } = useContext(MyContext);
429 | return (
430 |
431 |
{state.number}
432 |
setState({ number: state.number + 1 })}>+
433 |
434 | )
435 | }
436 | ```
437 |
438 | #### ‼️性能优化
439 |
440 | useContext可能会导致组件频繁渲染,此时可以将其他函数或者对象进行memo处理。
441 |
442 | - [ ] 性能优化
443 |
444 | ### useEffect
445 |
446 | #### 能力层面
447 |
448 | - 该 Hook 接收一个包含命令式、且可能有副作用代码的函数
449 |
450 | * 在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性
451 | * 使用 useEffect 完成副作用操作,赋值给 useEffect 的函数**会在组件渲染到屏幕之后执行**。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道
452 | * useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 `componentDidMount`、`componentDidUpdate` 和 `componentWillUnmount` 具有相同的用途,只不过被合并成了一个 API
453 |
454 | #### 使用层面
455 |
456 | *
457 |
458 | ```
459 | useEffect(didUpdate);
460 | ```
461 |
462 | #### demo: 实现修该标题
463 |
464 | ```
465 | class Counter extends React.Component {
466 | constructor(props) {
467 | super(props);
468 | this.state = {
469 | number: 0
470 | };
471 | }
472 |
473 | componentDidMount() {
474 | document.title = `你点击了${this.state.number}次`;
475 | }
476 |
477 | componentDidUpdate() {
478 | document.title = `你点击了${this.state.number}次`;
479 | }
480 |
481 | render() {
482 | return (
483 |
484 |
{this.state.number}
485 |
this.setState({ number: this.state.number + 1 })}>
486 | +
487 |
488 |
489 | );
490 | }
491 | }
492 | import React,{Component,useState,useEffect} from 'react';
493 | import ReactDOM from 'react-dom';
494 | function Counter(){
495 | const [number,setNumber] = useState(0);
496 | // 相当于 componentDidMount 和 componentDidUpdate:
497 | useEffect(() => {
498 | // 使用浏览器的 API 更新页面标题
499 | document.title = `你点击了${number}次`;
500 | });
501 | return (
502 | <>
503 | {number}
504 | setNumber(number+1)}>+
505 | >
506 | )
507 | }
508 | ReactDOM.render(, document.getElementById('root'));
509 | ```
510 |
511 | #### ‼️最佳实践
512 |
513 | > 在这个 class 中,我们需要在两个生命周期函数中编写重复的代码, 这是因为很多情况下,我们希望在组件加载和更新时执行同样的操作。我们希望它在每次渲染之后执行,但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。useEffect 会在第一次渲染之后和每次更新之后都会执行
514 | > 每次我们重新渲染,都会生成新的 effect,替换掉之前的。某种意义上讲,effect 更像是渲染结果的一部分 —— 每个 effect 属于一次特定的渲染。
515 |
516 | #### ‼️性能优化
517 |
518 | * 如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可
519 | * 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行
520 |
521 | ```
522 | function Counter(){
523 | const [number,setNumber] = useState(0);
524 | // 相当于componentDidMount 和 componentDidUpdate
525 | useEffect(() => {
526 | console.log('开启一个新的定时器')
527 | const $timer = setInterval(()=>{
528 | setNumber(number=>number+1);
529 | },1000);
530 | },[]);
531 | return (
532 | <>
533 | {number}
534 | >
535 | )
536 | }
537 | ```
538 |
539 | #### ‼️清除副作用
540 |
541 | * 副作用函数还可以通过返回一个函数来指定如何清除副作用
542 | * 为防止内存泄漏,清除函数会在组件卸载前执行。另外,如果组件多次渲染,则在执行下一个 effect 之前,上一个 effect 就已被清除
543 |
544 | ```
545 | import React, { useEffect, useState, useReducer } from 'react';
546 | import ReactDOM from 'react-dom';
547 | function Counter() {
548 | const [number, setNumber] = useState(0);
549 | useEffect(() => {
550 | console.log('开启一个新的定时器')
551 | const $timer = setInterval(() => {
552 | setNumber(number => number + 1);
553 | }, 1000);
554 | return () => {
555 | console.log('销毁老的定时器');
556 | clearInterval($timer);
557 | }
558 | });
559 | return (
560 | <>
561 | {number}
562 | >
563 | )
564 | }
565 | function App() {
566 | let [visible, setVisible] = useState(true);
567 | return (
568 |
569 | {visible && }
570 | setVisible(false)}>stop
571 |
572 | )
573 | }
574 | ReactDOM.render(, document.getElementById('root'));
575 | ```
576 |
577 | ### useRef
578 |
579 | * useRef 返回一个可变的 ref 对象,其 `.current` 属性被初始化为传入的参数(initialValue)
580 | * 返回的 ref 对象在组件的整个生命周期内保持不变
581 |
582 | ```
583 | const refContainer = useRef(initialValue);
584 | ```
585 |
586 | #### useRef [#](#t217.5.1 useRef)
587 |
588 | ```
589 | import React, { useState, useEffect, useRef } from 'react';
590 | import ReactDOM from 'react-dom';
591 | function Parent() {
592 | let [number, setNumber] = useState(0);
593 | return (
594 | <>
595 |
596 | setNumber({ number: number + 1 })}>+
597 | >
598 | )
599 | }
600 | let input;
601 | function Child() {
602 | const inputRef = useRef();
603 | console.log('input===inputRef', input === inputRef);
604 | input = inputRef;
605 | function getFocus() {
606 | inputRef.current.focus();
607 | }
608 | return (
609 | <>
610 |
611 | 获得焦点
612 | >
613 | )
614 | }
615 | ReactDOM.render(, document.getElementById('root'));
616 | ```
617 |
618 | #### forwardRef [#](#t227.5.2 forwardRef)
619 |
620 | * 将 ref 从父组件中转发到子组件中的 dom 元素上
621 | * 子组件接受 props 和 ref 作为参数
622 |
623 | ```
624 | function Child(props,ref){
625 | return (
626 |
627 | )
628 | }
629 | Child = forwardRef(Child);
630 | function Parent(){
631 | let [number,setNumber] = useState(0);
632 | const inputRef = useRef();
633 | function getFocus(){
634 | inputRef.current.value = 'focus';
635 | inputRef.current.focus();
636 | }
637 | return (
638 | <>
639 |
640 | setNumber({number:number+1})}>+
641 | 获得焦点
642 | >
643 | )
644 | }
645 | ```
646 |
647 | #### useImperativeHandle [#](#t237.5.3 useImperativeHandle)
648 |
649 | * `useImperativeHandle` 可以让你在使用 ref 时自定义暴露给父组件的实例值
650 | * 在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用
651 |
652 | ```
653 | function Child(props,ref){
654 | const inputRef = useRef();
655 | useImperativeHandle(ref,()=>(
656 | {
657 | focus(){
658 | inputRef.current.focus();
659 | }
660 | }
661 | ));
662 | return (
663 |
664 | )
665 | }
666 | Child = forwardRef(Child);
667 | function Parent(){
668 | let [number,setNumber] = useState(0);
669 | const inputRef = useRef();
670 | function getFocus(){
671 | console.log(inputRef.current);
672 | inputRef.current.value = 'focus';
673 | inputRef.current.focus();
674 | }
675 | return (
676 | <>
677 |
678 | setNumber({number:number+1})}>+
679 | 获得焦点
680 | >
681 | )
682 | }
683 | ```
684 |
685 | ### useLayoutEffect
686 |
687 | ----------------------------------------------
688 |
689 | * 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect
690 | * 可以使用它来读取 DOM 布局并同步触发重渲染
691 | * 在浏览器执行绘制之前 useLayoutEffect 内部的更新计划将被同步刷新
692 | * 尽可能使用标准的 useEffect 以避免阻塞视图更新
693 |
694 | 
695 |
696 | ```
697 | function LayoutEffect() {
698 | const [color, setColor] = useState('red');
699 | useLayoutEffect(() => {
700 | alert(color);
701 | });
702 | useEffect(() => {
703 | console.log('color', color);
704 | });
705 | return (
706 | <>
707 | 颜色
708 | setColor('red')}>红
709 | setColor('yellow')}>黄
710 | setColor('blue')}>蓝
711 | >
712 | );
713 | }
714 | ```
715 |
716 | ### 自定义 Hook
717 |
718 | * 有时候我们会想要在组件之间重用一些状态逻辑
719 | * 自定义 Hook 可以让你在不增加组件的情况下达到同样的目的
720 | * Hook 是一种复用状态逻辑的方式,它不复用 state 本身
721 | * 事实上 Hook 的每次调用都有一个完全独立的 state
722 | * 自定义 Hook 更像是一种约定,而不是一种功能。如果函数的名字以 use 开头,并且调用了其他的 Hook,则就称其为一个自定义 Hook
723 |
724 | #### demo:自定义计数器
725 |
726 | ```
727 | function useNumber(){
728 | const [number,setNumber] = useState(0);
729 | useEffect(() => {
730 | console.log('开启一个新的定时器')
731 | const $timer = setInterval(()=>{
732 | setNumber(number+1);
733 | },1000);
734 | return ()=>{
735 | console.log('销毁老的定时器')
736 | clearInterval($timer);
737 | }
738 | });
739 | return number;
740 | }
741 | function Counter1(){
742 | let number1 = useNumber();
743 | return (
744 | <>
745 | {number1}
746 | >
747 | )
748 | }
749 | function Counter2(){
750 | let number = useNumber();
751 | return (
752 | <>
753 | {number}
754 | >
755 | )
756 | }
757 | function App(){
758 | return <>>
759 | }
760 | ```
761 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-advanced",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "express": "^4.17.1",
7 | "react": "^16.12.0",
8 | "react-dom": "^16.12.0",
9 | "react-scripts": "3.2.0"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test",
15 | "eject": "react-scripts eject"
16 | },
17 | "eslintConfig": {
18 | "extends": "react-app"
19 | },
20 | "browserslist": {
21 | "production": [
22 | ">0.2%",
23 | "not dead",
24 | "not op_mini all"
25 | ],
26 | "development": [
27 | "last 1 chrome version",
28 | "last 1 firefox version",
29 | "last 1 safari version"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullStack-feed/react-advanced/0952fa2dcc2cb6b28313f827fdb25528a3448b2a/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullStack-feed/react-advanced/0952fa2dcc2cb6b28313f827fdb25528a3448b2a/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fullStack-feed/react-advanced/0952fa2dcc2cb6b28313f827fdb25528a3448b2a/public/logo512.png
--------------------------------------------------------------------------------
/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 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/BrowserRouter.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | export default class HashRouter extends Component {
4 | state = {
5 | location: {
6 | pathname: document.location.pathname,
7 | state: undefined
8 | }
9 | }
10 | componentDidMount() {
11 | window.onpopstate = (event) => {
12 | this.setState({
13 | location: {
14 | ...this.state.location,
15 | pathname: document.location.pathname,
16 | state: event.state
17 | }
18 | });
19 | }
20 | window.onpushstate = ({ state, pathname }) => {
21 | this.setState({
22 | location: {
23 | ...this.state.location,
24 | pathname,
25 | state
26 | }
27 | });
28 | }
29 | }
30 | render() {
31 | let value = {
32 | location: this.state.location,
33 | history: {
34 | location: this.state.location,
35 | push: (to) => {
36 | if (typeof to === 'object') {
37 | let { pathname, state } = to;
38 | window.history.pushState(state, '', pathname);
39 | } else {
40 | window.history.pushState('', '', to);
41 | }
42 | }
43 | }
44 | }
45 | return (
46 |
47 | {this.props.children}
48 |
49 | )
50 | }
51 | }
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/HashRouter.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | export default class HashRouter extends Component {
4 | state = {
5 | location: {
6 | pathname: window.location.hash.slice(1) || '/',
7 | state: undefined
8 | }
9 | }
10 | componentDidMount() {
11 | window.addEventListener('hashchange', event => {
12 | this.setState({
13 | location: {
14 | ...this.state.location,
15 | state: this.locationState,
16 | pathname: window.location.hash.slice(1) || '/'
17 | }
18 | });
19 | });
20 | window.location.hash = window.location.hash || '/';
21 | }
22 | render() {
23 | let value = {
24 | location: this.state.location,
25 | history: {
26 | location: this.state.location,
27 | /**
28 | * 兼容 字符串 & 对象的使用方式
29 | */
30 | push: (to) => {
31 | if (typeof to === 'object') {
32 | let { pathname, state } = to;
33 | this.locationState = state;
34 | window.location.hash = pathname;
35 | } else {
36 | window.location.hash = to;
37 | }
38 | }
39 | }
40 | }
41 | return (
42 |
43 | {this.props.children}
44 |
45 | )
46 | }
47 | }
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/Link.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | export default class Link extends Component {
4 | static contextType = Context;
5 | /**
6 | * - 需要获取当前history.push方法
7 | * - 监听onClick事件
8 | * - 将props中的to 传递给history.push 进行路由跳转
9 | */
10 | render() {
11 | return (
12 | this.context.history.push(this.props.to)}
14 | >
15 | {this.props.children}
16 |
17 | )
18 | }
19 | }
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/Redirect.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | /**
4 | * 相对简单,只是一个普通组件
5 | *
6 | * 在组件挂载时执行history.push方法
7 | */
8 | export default class extends Component {
9 | static contextType = Context;
10 | componentDidMount() {
11 | this.context.history.push(this.props.to);
12 | }
13 | render() {
14 | return null;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/Route.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | import { pathToRegexp } from 'path-to-regexp';
4 | //如果你给类增加了一个static contextType,this.context= Provider.value
5 | export default class Route extends Component {
6 | static contextType = Context;
7 | render() {
8 | let location = this.context.location;
9 | let history = this.context.history;
10 | let pathname = location.pathname;
11 | let { path = "/", component: RouteComponent, exact = false, render, children } = this.props;
12 | let keys = [];
13 | let regexp = pathToRegexp(path, keys, { end: exact });
14 | let result = pathname.match(regexp);
15 | let props = {
16 | location,
17 | history,
18 | }
19 | if (result) {
20 | //处理match params
21 | let [url, ...values] = result;//
22 | let paramNames = keys.map(item => item.name);
23 | let params = paramNames.reduce((acc, curr, index) => {
24 | acc[curr] = values[index];
25 | return acc;
26 | }, {});
27 | let match = {
28 | url,//匹配的URL地址
29 | path,//匹配的路径
30 | isExact: pathname === url,//是否精确匹配
31 | params//匹配的路径参数对象
32 | }
33 | props.match = match;
34 | if (RouteComponent) {
35 | return
36 | } else if (render) {
37 | return render(props);
38 | } else if (children) {
39 | return children(props);
40 | } else {
41 | return null;
42 | }
43 | } else {
44 | if (children) {
45 | return children(props);
46 | } else {
47 | return null;
48 | }
49 | }
50 | return null;
51 | }
52 | }
--------------------------------------------------------------------------------
/src/React-Router/react-router-dom/Switch.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Context from './context';
3 | import { pathToRegexp } from 'path-to-regexp';
4 | export default class Switch extends Component {
5 | static contextType = Context;
6 | render() {
7 | let location = this.context.location;
8 | let pathname = location.pathname;
9 | if (!this.props.children) {
10 | return null;
11 | }
12 | /**
13 | * 循环子组件,匹配对应的路由
14 | *
15 | * 将路由返回给History/BrowserRouter执行
16 | */
17 | for (let i = 0; i < this.props.children.length; i++) {
18 | let child = this.props.children[i];
19 | let { path = "/", component: RouteComponent, exact = false } = child.props;
20 | let regexp = pathToRegexp(path, [], { end: exact });
21 | let result = pathname.match(regexp);
22 | if (result) {
23 | return child;
24 | }
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/React-Router/路由基础原理/hash.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | React App
10 |
11 |
12 |
13 | 去/a
14 | 去/b
15 |
16 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/React-Router/路由基础原理/history.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/Redux-React/components/connect.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from '../react-redux';
3 | import actions from '../store/actions/counter1';
4 | /**
5 | * 通常在组件使用原生Redux时需要做如下四件事儿:
6 | *
7 | * - 引入store
8 | * - 将需要的状态映射成组件内部的状态 (this.state = store.getState())
9 | - 当状态发生改变后,需要手动 订阅和取消订阅 一个“视图更新”函数,例如this.setState()
10 | * - bindActionCreators,使得组件内部方便执行actions
11 | *
12 | * 如下两点是有区别的:
13 |
14 | * - 绑定的actions不一样(来自不同的action)
15 | *
16 | * - 映射的状态不同
17 | */
18 | class Counter1 extends Component {
19 | render() {
20 | return (
21 |
22 |
{this.props.name}:{this.props.number}
23 |
this.props.add1(5)}>+
24 |
this.props.thunkAdd(5)}>thunkAdd
25 |
this.props.promiseAdd(5)}>promiseAdd
26 |
27 | )
28 | }
29 | }
30 |
31 | // 通过connect函数会自动映射到组件的props中
32 |
33 | const mapStateToProps = state => state.counter1;
34 | export default connect(
35 | mapStateToProps,
36 | actions
37 | )(Counter1);
--------------------------------------------------------------------------------
/src/Redux/components/1.Redux-base.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { createStore } from 'redux';
3 | // action-types
4 | const ADD = 'ADD';
5 | const MINUS = 'MINUS';
6 | // reducer
7 | function counter(state, action) {
8 | switch (action.type) {
9 | case ADD:
10 | return { number: state.number + 1 }
11 | break;
12 | case MINUS:
13 | return { number: state.number - 1 }
14 | default:
15 | break;
16 | }
17 | }
18 | // store
19 | let store = createStore(counter, { number: 0 });
20 |
21 | class Counter extends Component {
22 | constructor() {
23 | super(props)
24 | this.state = {
25 | number: store.getState().number
26 | }
27 | }
28 | componentDidMount() {
29 | // 订阅函数,在Redux内部中,数据发生改变后要刷新这个函数
30 | this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().number }))
31 | }
32 | componentWillUnmount() {
33 | this.unsubscribe();
34 | }
35 | render() {
36 | return (
37 |
38 | {/*
39 | 修改store中的数据唯一方式:
40 | dispatch 一个 action
41 | action 就是一个type(必有) + payload(可无)
42 | */}
43 | store.dispatch({ type: 'ADD' })}>
44 | store.dispatch({ type: 'MINUS' })} >
45 |
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Redux/components/2.bindActionCreators-base.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import actions from '../store-模块化reducer/actions/counter1';
3 | import { createStore, bindActionCreators } from 'redux';
4 |
5 | function counterReducer(state, action) {
6 | switch (action.type) {
7 | case ADD:
8 | return { number: state.number + action.amount }
9 | break;
10 | case MINUS:
11 | return { number: state.number - action.amount }
12 | default:
13 | break;
14 | }
15 | }
16 | let store = createStore(counterReducer, { number: 0 });
17 | // action 加持 store.dispatch 仅此而已。
18 | let boundActions = bindActionCreators(actions, store.dispatch);
19 | class Counter extends Component {
20 | constructor() {
21 | super(props)
22 | this.state = {
23 | number: store.getState().number
24 | }
25 | }
26 | componentDidMount() {
27 | this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().number }))
28 | }
29 | componentWillUnmount() {
30 | this.unsubscribe();
31 | }
32 |
33 |
34 | render() {
35 | return (
36 |
37 | boundActions.add(1)}>
38 | boundActions.minus(2)} >
39 |
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Redux/components/3.模块化reducer后的使用.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import actions from '../store-模块化reducer/actions/counter1';
3 | import { bindActionCreators } from '../redux';
4 | import store from '../store-模块化reducer'
5 | let boundActions = bindActionCreators(actions, store.dispatch);
6 | class Counter extends Component {
7 | constructor() {
8 | super(props)
9 | this.state = {
10 | // 这里需要取对应的state
11 | number: store.getState().counter1.number
12 | }
13 | }
14 | componentDidMount() {
15 | this.unsubscribe = store.subscribe(() => this.setState({ number: store.getState().counter1.number }))
16 | }
17 | componentWillUnmount() {
18 | this.unsubscribe();
19 | }
20 |
21 |
22 | render() {
23 | return (
24 |
25 | boundActions.add(1)}>
26 | boundActions.minus(2)} >
27 |
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Redux/components/4.React-Redux.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { connect } from '../react-redux';
3 | import actions from '../store/actions/counter1';
4 | /**
5 | * 所有用到Redux中状态的组件都要:
6 | *
7 | * 1.引入仓库
8 | * 2.都要订阅状态事件(刷新视图)和取消订阅(组件卸载时)
9 | * 3.都要获取仓库中的状态 映射为当前组件状态 (this.state = store.getState())
10 | * 4.都要绑定actions(bindActionCreators,使得组件内部方便执行actions)
11 | *
12 | * 不同点
13 | * 1. 绑定的actions不一样(来自不同的action)
14 | *
15 | * 2. 获取的子状态不一样
16 | */
17 | class Counter1 extends Component {
18 | render() {
19 | return (
20 |
21 |
{this.props.name}:{this.props.number}
22 |
this.props.add1(5)}>+
23 |
this.props.thunkAdd(5)}>thunkAdd
24 |
this.props.promiseAdd(5)}>promiseAdd
25 |
26 | )
27 | }
28 | }
29 | //输入就是把仓库中的状态进行计算或者说过滤变成当前组件的属性
30 | // 等同于? this.props = state.counter1={number:0};
31 | const mapStateToProps = state => state.counter1;//{number:0}
32 | export default connect(
33 | mapStateToProps,
34 | actions //里面会进行自动绑定
35 | )(Counter1);
--------------------------------------------------------------------------------
/src/Redux/react-redux/Provider.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Context from './context'
3 |
4 | /**
5 | * - Context 作为 provider
6 | * - 渲染子组件
7 | *
8 | *
9 | */
10 | // TODO: HOC???
11 | export default class extends React.Component {
12 | render() {
13 | return (
14 |
15 | {/* 插槽:子组件 */}
16 | {this.props.children}
17 |
18 | )
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Redux/react-redux/connect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Context from './context';
3 | import { bindActionCreators } from '../redux';
4 | /**
5 | *
6 | * @param {*} mapStateToProps 可以把完整的状态对象映射为组件属性对象
7 | * @param {*} mapDispatchToProps 是一个对象,里面的属性是actionCreator,返回一个对象{}
8 | * 把dispatch映射为了一个属性对象
9 | */
10 | export default function (mapStateToProps, mapDispatchToProps) {
11 | return function (OldComponent) {
12 | class Proxy extends React.Component {
13 | static contextType = Context
14 | constructor(props, context) {
15 | super(props);
16 | this.state = mapStateToProps(context.store.getState());
17 | }
18 | componentDidMount() {
19 | //每当仓库中的状态发生改变的时候,会执行此回调函数
20 | this.unsubscribe = this.context.store.subscribe(() => {
21 | this.setState(mapStateToProps(this.context.store.getState()));
22 | });
23 | }
24 | componentWillUnmount() {
25 | this.unsubscribe();
26 | }
27 | render() {
28 | let actions = bindActionCreators(
29 | mapDispatchToProps,
30 | this.context.store.dispatch);
31 | return
32 | }
33 | }
34 |
35 | // 通过向Proxy挂载静态属性即可实现在组件内部通过:
36 | // context
37 | Proxy.contextType = Context;
38 | return Proxy;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Redux/react-redux/context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // 创建一个Context - Provider
3 | export default React.createContext();
--------------------------------------------------------------------------------
/src/Redux/react-redux/index.js:
--------------------------------------------------------------------------------
1 | import Provider from './Provider';
2 | import connect from './connect';
3 | export {
4 | connect,
5 | Provider
6 | }
--------------------------------------------------------------------------------
/src/Redux/redux/applymiddleware.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 1.收集所有中间件
4 | * 2.接收老的createStore 以便在内部能够创建store 重写dispatch方法
5 | * 该方法接收 initalState + reducer
6 | */
7 | export default applymiddleware = (...middleware) => createStore => (reduer,initalState) => {
8 | let store = new createStore(reducer,initalState)
9 | let dispatch = undefined;
10 | let middlewareAPI = {
11 | getState: store.getState,
12 | dispatch: action => dispatch(action)
13 | }
14 | // 让中间件缓存API
15 | middlewares = middleware.map(m => m(middlewareAPI));
16 | // 让多个中间件串联,返回一个a(b(c())) 让该函数执行后,将store.dispatch传递进去,使得能够获得正常的dispatch方法
17 | dispatch = compose(...middlewares)(store.dispatch);
18 | return {
19 | ...store,
20 | dispatch
21 | }
22 | }
23 |
24 | const compose = (...func) => func.reduce((a,b) => (...args) => a(b(...args)))
25 |
26 |
--------------------------------------------------------------------------------
/src/Redux/redux/bindActionCreators.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 包裹函数,将actions中每一个action使用dispatch函数映射一下
3 | *
4 | * @param {*} actions action集合,保存着所有action函数
5 | * @param {*} dispatch
6 | */
7 | export default function bindActionCreators(actions, dispatch) {
8 | // 用于保存所有被dispatch包装过的action集合
9 | let boundActions = {};
10 | for (const action in actions) {
11 | // 注意 此处需要透传 原 action中参数
12 | boundActions[action] = (...args) => {
13 | dispatch(actions[action](...args))
14 | }
15 | }
16 | return boundActions;
17 | }
--------------------------------------------------------------------------------
/src/Redux/redux/combineReducers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 返回一个函数,执行该函数能够获取最新的数据
3 | * @param {object} reducers redcer的集合
4 | */
5 | export default function combineReducers(reducers) {
6 | return (state = {}, action) => {
7 | let newGlobalState = {};
8 | for (let reducer in reducers) {
9 | // 拿到真正的reducer函数
10 | let reducer = reducers[reducer];
11 | let localPrevState = state[reducer];
12 | let localNextState = reducer(localPrevState, action);
13 | newGlobalState[reducer] = localNextState;
14 | }
15 | return newGlobalState
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Redux/redux/compose.js:
--------------------------------------------------------------------------------
1 | export const compose = (...func) => func.reduce((a,b) => (...args) => a(b(...args)))
--------------------------------------------------------------------------------
/src/Redux/redux/createStore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 创建Redux 仓库,以及提供一个对象,对象拥有:
3 | * - 获取仓库的store数据:getState()
4 | * - 修改store数据的唯一方式:dispatch()
5 | * - 预定状态发生改变后的回调函数:subscribe()
6 | *
7 | * @param {*} reducer 和 reduce 什么关系来的???
8 | * @param {*} initialState
9 | * @param {*} enhancer
10 | */
11 | export default function createStore(reducer, initialState, enhancer) {
12 | let _state = initialState || {};
13 | let listeners = [];
14 |
15 | /**
16 | * 我们要想让仓库创建的时候就得到初始状态,就需要在创建时先派发一个初始化的动作
17 | *
18 | NOTE: 此时的state就会变为初始状态,可以打一个debugger 看一下子。
19 | */
20 | dispatch({ type: '@@REDUX/INIT' });
21 |
22 |
23 | /**
24 | * 通过此方法可以获得仓库中的最新的状态
25 | *
26 | * 简单来说就是 不能将state 直接暴露给外界,这样直接避免了被修改的可能性。
27 | *
28 | * 有点类似:Object.defineProperty value get set 的意思
29 | */
30 | function getState() {
31 | return _state;
32 | }
33 | /**
34 | * 通过此方法可以向仓库派发修改状态的action
35 | *
36 | * 为了能够状态改变后自动刷新依赖,需要在内部刷新所有listeners [发布订阅]
37 | *
38 | * @param {*} action
39 | */
40 | function dispatch(action) {
41 | // 通过reducer 拉取状态变更后的新状态
42 | _state = reducer(_state, action);
43 | // 通知所有订阅的函数
44 | listeners.forEach(listener => listener());
45 | }
46 | /**
47 | * 当用户想订阅仓库中的状态变化事件的时候,就可以订阅这个状态变化事件
48 | *
49 | * 需要传递个回调/监听函数
50 | *
51 | * - 缓存监听函数
52 | *
53 | * - 返回一个取消函数,当执行该取消函数后会将当前的listener 从 监听数组中删除
54 | *
55 | * @param {*} listener 监听函数
56 | * @returns {function} unsubscribe 取消订阅函数
57 | */
58 | function subscribe(listener) {
59 | listeners.push(listener);
60 | return () => {
61 | let index = listeners.indexOf(listener);
62 | listeners.splice(index, 1)
63 | }
64 | }
65 | return {
66 | getState,
67 | dispatch,
68 | subscribe
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Redux/redux/index.js:
--------------------------------------------------------------------------------
1 | export { default as createStore } from './createStore';
2 | export { default as bindActionCreators } from './bindActionCreators';
3 | export { default as combineReducers } from './combineReducers';
4 | export { default as applyMiddleware } from './applyMiddleware';
5 | export { default as compose } from './compose';
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/action-types.js:
--------------------------------------------------------------------------------
1 | export const ADD1 = 'ADD1';
2 | export const MINUS1 = 'MINUS1';
3 |
4 | export const ADD2 = 'ADD2';
5 | export const MINUS2 = 'MINUS2';
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/actions/counter1.js:
--------------------------------------------------------------------------------
1 | import * as ACTION_TYPES from '../action-types';
2 | function add1(amount) {
3 | return { type: ACTION_TYPES.ADD1, amount };
4 | }
5 | function minus1(amount) {
6 | return { type: ACTION_TYPES.MINUS1, amount };
7 | }
8 | export default { add1, minus1 }
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/actions/counter2.js:
--------------------------------------------------------------------------------
1 | import * as ACTION_TYPES from '../action-types';
2 | function add2(amount) {
3 | return { type: ACTION_TYPES.ADD2, amount };
4 | }
5 | function minus2(amount) {
6 | return { type: ACTION_TYPES.MINUS2, amount };
7 | }
8 | export default {
9 | add2,
10 | minus2
11 | }
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/index.js:
--------------------------------------------------------------------------------
1 | import reducer from './reducers';
2 | import { createStore } from '../redux';
3 |
4 | let store = createStore(reducer, {})
5 | export default store;
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/reducers/counter1.js:
--------------------------------------------------------------------------------
1 | import * as ACTION_TYPES from '../action-types'
2 |
3 | let initialState = { number: 0 };
4 |
5 | export default function counter1(state = initialState, action) {
6 | switch (action.type) {
7 | case ACTION_TYPES.ADD1:
8 | return { number: state.number + action.amount };
9 | case ACTION_TYPES.MINUS1:
10 | return { number: state.number - action.amount };
11 | default:
12 | return state
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/reducers/counter2.js:
--------------------------------------------------------------------------------
1 | import * as ACTION_TYPES from '../action-types';
2 | let initialState = { number: 0 };
3 | export default function counter2(state = initialState, action) {
4 | switch (action.type) {
5 | case ACTION_TYPES.ADD2:
6 | return { number: state.number + action.amount };
7 | case ACTION_TYPES.MINUS2:
8 | return { number: state.number - action.amount };
9 | default:
10 | return state;
11 | }
12 | }
--------------------------------------------------------------------------------
/src/Redux/store-模块化reducer/reducers/index.js:
--------------------------------------------------------------------------------
1 | import counter1 from './counter1';
2 | import counter2 from './counter2';
3 | import { combineReducers } from '../../redux';
4 |
5 | let reducers = { counter1, counter2 };
6 | // 合并reducer后导出,使得createReducer能够接受一个根reducer(汇总后的)
7 | let reducer = combineReducers(reducers);
8 | export default reducer;
--------------------------------------------------------------------------------
/src/Redux/中间件函数/index.js:
--------------------------------------------------------------------------------
1 | import { createElement } from "react";
2 | import { compose } from "../redux/compose";
3 |
4 | const { applyMiddleware, createStore } = require("../redux");
5 |
6 | const looger = ({getState}) => next => action => {
7 | // 此处即使增强后的dispatch方法
8 | console.log('改变前的状态:',getState());
9 | next(action);
10 | console.log('改变后的状态:',getState());
11 | }
12 | const thunk = ( {getState,dispatch} ) => next => action => {
13 | /**
14 | * 通常 thunk函数在内部会做一些额外逻辑后,在内部会手动调用dispatch(action);
15 | * 因此只需要将getState dispatch 传递给thunk函数即可
16 | */
17 | if(typeof action === 'function') {
18 | action({getState,dispatch})
19 | }
20 | // 否则的话直接调用next
21 | next(action);
22 | }
23 | const promise = ({dispatch}) => next => action => {
24 | // 支持action中拥有then函数
25 | if(action.then && typeof action.then === 'function') {
26 | action.then(action => dispatch(action))
27 | }
28 | next(action);
29 | }
30 |
--------------------------------------------------------------------------------
/src/hooks/useCallback/index.jsx:
--------------------------------------------------------------------------------
1 | // import React, { useState, useMemo, memo, useCallback } from "react";
2 | // let Child = (props) => {
3 | // console.log("Child组件渲染了");
4 | // return {props.data.number};
5 | // };
6 | // //就让函数组件拥有了记忆的功能,只有当组件的属性发生变更的时候才会刷新,否则不刷新
7 | // // 但由于Child组件接受了一个函数作为props的成员,所以在App组件刷新时如果没有对函数做优化,依然会导致Child组件刷新
8 | // Child = memo(Child);
9 | // let lastAddClick;
10 | // let lastData;
11 | // export const UseMemoPerformance = () => {
12 | // let [number, setNumber] = useState(0);
13 | // let [name, setName] = useState("zhufeng");
14 | // //第一个参数deps,表示此函数缓存依赖的变量,如果变量变了,会重新生成函数,否则每次的addClick函数会复用
15 | // // 这种情况下,函数内部的number没有发生联动,导致addClick并不能真正的设置setNumber的值
16 | // // const addClick = useCallback(() => setNumber(number + 1), []);
17 | // const addClick = useCallback(() => setNumber(number + 1), [number]);
18 | // console.log("lastAddClick === addClick", lastAddClick === addClick);
19 | // lastAddClick = addClick;
20 | // // 比useCallback厉害之处在于能够缓存函数的返回值,其解决的问题是一样的。
21 | // const data = useMemo(() => ({ number }), [number]);
22 | // console.log("lastData === data", lastData === data);
23 | // lastData = data;
24 | // return (
25 | //
26 | // setName(e.target.value)}
30 | // />
31 | //
32 | //
33 | // );
34 | // };
35 |
36 | // !!! useCallback 当内部使用了useState后,需要注意依赖项的问题
37 | // 这种情况下,函数内部的number没有发生联动,导致addClick并不能真正的设置setNumber的值
38 | // const addClick = useCallback(() => setNumber(number + 1), []);
39 |
--------------------------------------------------------------------------------
/src/hooks/useContext/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useReducer, useState, useContext } from 'react';
2 | // 语法糖而已,注意 供应商和消费者关系
3 | let MyContext = React.createContext();
4 |
5 | function App() {
6 | const [state, setState] = useState({ number: 0 });
7 | return (
8 | // 向下暴露数据
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | function Counter() {
16 | // 只是把MyContext解构出来,不要想多了
17 | // 不管父组件是否进行SCU优化,或者memo优化,只要MyContext发生改变,就会重新渲染该组件
18 | let { state, setState } = useContext(MyContext);
19 | return (
20 |
21 |
{state.number}
22 |
setState({ number: state.number + 1 })}>+
23 |
24 | )
25 | }
--------------------------------------------------------------------------------
/src/hooks/useMemo/index.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useMemo, memo, useCallback } from "react";
2 |
3 | // 注意:Child 接收了 addClick 函数作为参数
4 | // 如果没有对该函数做缓存,会导致 addClick 重复创建新的内存对象。
5 |
6 | let Child = ({ data, addClick }) => {
7 | console.log("Child组件渲染了");
8 | return {data.number};
9 | };
10 |
11 | // memo:让函数组件拥有了记忆的功能,只有当组件内部状态发生变更的时候才会重新渲染。
12 | // 但是,由于addClick 是一个函数,如果不做缓存处理,每次都是一个新的值,会导致Child组件渲染。
13 |
14 | Child = memo(Child);
15 |
16 | let lastAddClick, lastData;
17 |
18 | export const UseMemoPerformance = () => {
19 | let [number, setNumber] = useState(0);
20 | let [name, setName] = useState("");
21 |
22 | //第一个参数deps,表示此函数缓存依赖的项,依赖改变后才会创建新的函数。
23 | const addClick = useCallback(() => setNumber(number + 1), [number]);
24 |
25 | console.log("lastAddClick === addClick", lastAddClick === addClick);
26 |
27 | lastAddClick = addClick;
28 | // 比useCallback厉害之处在于能够缓存函数的返回值,其解决的问题是相同的(缓存)
29 | const data = useMemo(() => ({ number }), [number]);
30 | console.log("lastData === data", lastData === data);
31 | lastData = data;
32 | return (
33 |
34 | setName(e.target.value)}
38 | />
39 |
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/src/hooks/useReducer/index.js:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useReducer } from 'react';
2 | let initialState = { number: 0 };
3 | const INCREMENT = 'INCREMENT';
4 | const DECREMENT = 'DECREMENT';
5 | function reducer(state, action) {
6 | switch (action.type) {
7 | case INCREMENT:
8 | return { number: state.number + 1 };
9 | case DECREMENT:
10 | return { number: state.number - 1 };
11 | default:
12 | return state;
13 | }
14 | }
15 | //TODO: 什么时候使用useState 和 useReducer
16 | //useState 实现原理:
17 | const useState = (initialState) => {
18 | const reducer = useCallback((state, action) => action);
19 | let [state, dispatch] = useReducer(reducer, initialState);
20 | function setState(payload) {
21 | dispatch(payload);
22 | }
23 | return [state, setState];
24 | }
25 | const App = () => {
26 | let [state, dispatch] = useReducer(reducer, initialState);
27 | // let [state, setState] = useState(initialState);
28 | return (
29 |
30 |
{state.number}
31 |
setState({ number: state.number + 1 })}>+
32 |
setState(dispatch({ type: INCREMENT }))}>-
33 |
34 | )
35 | }
--------------------------------------------------------------------------------
/src/hooks/useState/counter.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback } from "react";
2 | export class CounterClass extends React.Component {
3 | constructor(props) {
4 | super(props);
5 | this.state = {
6 | number: 0,
7 | };
8 | }
9 | handleClick = () => {
10 | this.setState({ number: this.state.number + 1 });
11 | };
12 | render() {
13 | return (
14 |
15 |
{this.state.number}
16 |
点击 + 1
17 |
18 | );
19 | }
20 | }
21 | export const CounterHooks = () => {
22 | const [counter, setCounter] = useState(0);
23 | return (
24 |
25 |
{counter}
26 | {/* NOTE:
27 |
28 | setState函数在使用时候,要注意避免循环渲染,例如这样调用:
29 |
prevCounter + 1)}>
30 |
31 | Uncaught Error:
32 | Too many re-renders.
33 | React limits the number of renders to prevent an infinite loop.
34 | 也好理解:
35 | 首次渲染时候,会对JSX进行解析,解析到setCounter时,由于是个函数调用因此会将他执行,必然会导致死循环。
36 |
37 | */}
38 | setCounter((prevCounter) => prevCounter + 1)}>
39 | 点击 + 1
40 |
41 |
42 | );
43 | };
44 |
45 | export const CounterClosure = () => {
46 | let [state, setState] = useState(0);
47 | const alertNumber = () => {
48 | console.log(`点击alert后,此时作用域下的state为:`, state);
49 | setTimeout(() => {
50 | alert(state);
51 | }, 3000);
52 | };
53 | const handleClick = () => {
54 | console.log(`组件重新渲染, 新的作用域下的state为:`, state);
55 | setState((lastState) => lastState + 1);
56 | };
57 | return (
58 |
59 |
当前state中的number为:{state}
60 |
点击我创建新的作用域并将状态加1
61 |
点击我锁定当前作用域并准备alert
62 |
63 | );
64 | };
65 | export const CounterUpdateFunctional = () => {
66 | let [state, setState] = useState({ number: 0 });
67 | // 无法跟进当前页面最新值,会将当前页面的值修改到触发时锁定的作用域内的值
68 | function lazy() {
69 | setTimeout(() => {
70 | setState({ number: state.number + 1 });
71 | }, 1000);
72 | }
73 | // 能够跟进当前组件的状态,保证累加到当前渲染的组件身上
74 | function lazyFunction() {
75 | setTimeout(() => {
76 | setState((state) => ({ number: state.number + 1 }));
77 | }, 3000);
78 | }
79 | return (
80 |
81 |
{state.number}
82 |
83 |
setState({ number: state.number + 1 })}>
84 | 点我正常增加
85 |
86 |
点我会锁定当前增加的值
87 |
点我会在正常增加基础上再增加
88 |
89 | );
90 | };
91 |
92 | export const CounterLazyNoMergeState = () => {
93 | let [state, setState] = useState(function () {
94 | console.log("只有在首次渲染组件时会被执行");
95 | return { number: 0, name: "计数器" };
96 | });
97 | //1.initialState初始状态参数只会有组件初始渲染的时候调用,后续更新渲染时会被忽略
98 |
99 | //2.跟类组件setState不同,这里的状态不会自动合并 ,更新的时候要传入完整的值
100 |
101 | // eg:点击button1时此时name会消失
102 |
103 | return (
104 |
105 |
106 | {state.name}:{state.number}
107 |
108 |
setState({ number: state.number + 1 })}>
109 | 我更新后不会合并参数
110 |
111 |
setState({ ...state, number: state.number + 1 })}>
112 | 那就手动合并参数喽
113 |
114 |
115 | );
116 | };
117 | export const performanceCounter = () => {
118 | let [state, setState] = useState(function () {
119 | return { number: 0, name: "计数器" };
120 | });
121 | return (
122 |
123 |
124 | {state.name}:{state.number}
125 |
126 |
setState({ ...state, number: state.number + 1 })}>
127 | 不能进行深度比较
128 |
129 | {/* 此时会判定为相同对象,不会进行更新操作,使用的是Object.is方法来判断是否相同 */}
130 | {/* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is */}
131 |
setState(state)}>只能进行浅层比较
132 |
133 | );
134 | };
135 |
136 | let prevIncrease;
137 | let prevChangeName;
138 | export const performanceCacheCounter = () => {
139 | let [number, setNumber] = useState(0);
140 | let [name, setName] = useState("su");
141 | // const increase = () => setNumber(number + 1) // 不能被缓存的函数
142 | const increase = useCallback(() => setNumber(number + 1), [number]); // 能够被缓存的函数
143 | /**
144 | * 如果不用useCallback 每次的函数都不相同;
145 | *
146 | * 使用 useCallback 后,就能保证在依赖项不改变的情况下使用缓存的函数;
147 | *
148 | * */
149 |
150 | console.log("组件更新后,increase是否进行缓存 ? ", prevIncrease === increase);
151 | prevIncrease = increase;
152 | const changeName = useCallback(() => setName(Date.now()), [name]);
153 | console.log(
154 | "组件更新后,increase是否进行缓存 ? ",
155 | prevChangeName === changeName
156 | );
157 | prevChangeName = changeName;
158 | return (
159 |
160 |
161 | {name}:{number}
162 |
163 |
164 | 点我只更新number,按道理setName应该被缓存,不会重新创建
165 |
166 |
167 | 点我只更新name,按道理setNumber应该被缓存,不会重新创建
168 |
169 |
170 | );
171 | };
172 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useReducer } from 'react';
2 | import ReactDOM from 'react-dom';
3 | // import { CounterUpdateFunctional } from './hooks/useState/counter';
4 | import { UseMemoPerformance } from './hooks/useMemo/index.jsx'
5 | function App() {
6 | return
7 | Hey
8 |
9 | }
10 | ReactDOM.render(, document.getElementById('root'));
--------------------------------------------------------------------------------
/src/my_react/index.js:
--------------------------------------------------------------------------------
1 | import React from './react';
2 | import ReactDOM from './react-dom';
3 |
4 | let element = React.createElement(
5 | 'h1',
6 | { style: { color: "red", background: "green" } },
7 | 'hello',
8 | React.createElement('span', null, 'world')
9 | )
10 | console.log(element);
11 | // debugger
12 | ReactDOM.render(element, document.getElementById('root'));
--------------------------------------------------------------------------------
/src/my_react/react-dom/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * - 根据虚拟DOM创建真实DOM元素
3 | *
4 | * - 向目标节点挂载DOM
5 | *
6 | * @param {*} element
7 | * @param {*} parentNode
8 | * @param {*} componentInstance
9 | */
10 | function render(element, parentNode, componentInstance) {
11 | // 防止文本节点
12 | if (typeof element === "string" || typeof element === "number") {
13 | return parentNode.appendChild(document.createTextNode(element));
14 | }
15 | let type = element.type,
16 | props = element.props;
17 | // 兼容处理类组件和函数组件
18 | let isReactComponent = type.isReactComponent;
19 | if (isReactComponent) {
20 | // TODO: 这里不懂
21 | componentInstance = new type(props);
22 | element = componentInstance.render();
23 | type = element.type;
24 | props = element.props;
25 | } else if (typeof type === "function") {
26 | element = type(props);
27 | type = element.type;
28 | props = element.props;
29 | }
30 | let reallyDOM = createDOM(type, props, componentInstance);
31 | if (isReactComponent) componentInstance.dom = reallyDOM;
32 | parentNode.appendChild(reallyDOM);
33 | }
34 | /**
35 | *
36 | *
37 | * @param {*} type
38 | * @param {*} props
39 | * @param {*} componentInstance
40 | */
41 | export function createDOM(type, props, componentInstance) {
42 | let currentDOM = document.createElement(type);
43 | for (let propName in props) {
44 | if (propName === 'children') {
45 | // 开始递归创建子节点
46 | let childrenNode = props.children;
47 | // console.log(childrenNode);
48 | childrenNode.forEach((child) => { render(child, currentDOM, componentInstance) })
49 | } else if (propName === "style") {
50 | let styleObject = props.style;
51 | for (let styleAttr in styleObject) {
52 | currentDOM.style[styleAttr] = styleObject[styleAttr];
53 | }
54 | } else if (propName === "className") {
55 | currentDOM.className = props.className;
56 | } else if (propName.startsWith('on')) {
57 | // TODO: 事件处理
58 | } else {
59 | // 认为是自定义属性字段
60 | currentDOM.setAttribute(propName, props[propName])
61 | }
62 | }
63 | return currentDOM;
64 | }
65 | export default {
66 | render
67 | }
68 |
--------------------------------------------------------------------------------
/src/my_react/react/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { createDOM } from "../react-dom";
3 |
4 | /**
5 | * 功能:
6 | * - 创建虚拟DOM元素?(用于收集虚拟DOM元素的所有属性)
7 | * -
8 | *
9 | *
10 | *
11 | * @param {*} type
12 | * @param {*} config
13 | * @param {...any} children
14 | */
15 | function createElement(type, config, ...children) {
16 |
17 | let props = {
18 | ...config,
19 | children
20 | }
21 | let element = { type, props };
22 | return element;
23 | }
24 | class Component {
25 | static isReactComponent = true;
26 | constructor(props) {
27 | this.props = props;
28 | this.updateQueue = [];
29 | this.isBatchingUpdate = false;
30 | }
31 |
32 | /**
33 | * 1. 批量更新模式(同步)
34 | * @param partialState
35 | */
36 | setState(partialState) {
37 | this.updateQueue.push(partialState);
38 | if (!this.isBatchingUpdate) {
39 | } else {
40 | this.flushUpdateQueue();
41 | }
42 | }
43 |
44 | /**
45 | * 刷新所有缓存的partialState
46 | */
47 | flushUpdateQueue = () => {
48 | // this.updateQueue.reduce((memo,cur) => {
49 | // if(typeof cur === 'function') {
50 | // memo = {...memo,...cur(memo)};
51 | // }else {
52 | // // 数组和对象的情况下
53 | // memo = {...memo,...cur}
54 | // }
55 | // return memo;
56 | // // 这里的state也没定义
57 | // },this.state)
58 |
59 | //这样就不需要在手动的清空数组
60 | while (this.updateQueue.length) {
61 | let item = this.updateQueue.shift();
62 | if (typeof item === 'function') {
63 | this.state = { ...this.state, ...item(this.state) };
64 | } else {
65 | // 数组和对象的情况下
66 | this.state = { ...this.state, ...item }
67 | }
68 | return this.state;
69 | }
70 | renderComponent(this);
71 | }
72 | }
73 |
74 | /**
75 | *
76 | * @param componentInstance
77 | */
78 | function renderComponent(componentInstance) {
79 | // 获取新的DOM元素
80 | let renderElement = componentInstance.render();
81 | //
82 | let newDOM = createDOM(renderElement.type, renderElement.props, componentInstance);
83 | // 暴力更新, 老的DOM节点:componentInstance.dom
84 | componentInstance.dom.parentNode.replaceChild(newDOM, componentInstance.dom);
85 | // 更新老的节点为:newDOM
86 | componentInstance.dom = newDOM;
87 | }
88 |
89 | export default {
90 | createElement,
91 | Component
92 | }
93 |
--------------------------------------------------------------------------------