|
1 | 1 | Hooks 新人培训演讲大纲 |
2 | 2 |
|
3 | | -### React Logo 与 Hooks |
| 3 | +### Hooks |
4 | 4 |
|
5 | 5 |  |
6 | 6 |
|
7 | | -React 的 logo 是一个原子图案, 原子组成了物质。类似的, React 就如原子般构成了页面的表现; 而 Hooks 就如夸克, 其更接近 React 本质的样子, 但是直到 4 年后的今天才被真正设计出来。 —— Dan in React Conf(2018) |
| 7 | +React 的 logo 是一个原子图案, 原子组成了物质。类似的, React 就如原子般构成了页面的表现; 而 Hooks 就如夸克, 其更接近 React 本质的样子, 但是直到 2019 年(花了近 4 年时间)才被真正设计出来。 —— Dan in React Conf(2018) |
8 | 8 |
|
9 | | -### why Hooks? |
| 9 | +### React 中的逻辑复用 |
10 | 10 |
|
11 | | -一: `多个组件间逻辑复用`: 在 Class 中使用 React 不能将带有 state 的逻辑给单独抽离成 function, 其只能通过嵌套组件的方式来解决多个组件间逻辑复用的问题, 基于嵌套组件的思想存在 [HOC](https://github.com/MuYunyun/blog/blob/master/React/从0到1实现React/8.HOC探索.md) 与 `render props` 两种设计模式。但是这两种设计模式是否存在缺陷呢? |
| 11 | +在 Hooks 出来之前, 类组件是如何进行逻辑复用的呢? |
12 | 12 |
|
13 | | -* 嵌套地狱, 当嵌套层级过多后, 数据源的追溯会变得十分困难, 导致定位 bug 不容易; (hoc、render props) |
| 13 | +熟悉 React 的同学可能知道在 React 的设计理念中, 社区推崇使用组合而非继承的方式来达到逻辑复用的目的。[HOC](https://github.com/MuYunyun/blog/blob/master/React/从0到1实现React/8.HOC探索.md) 与 [Render Props](https://github.com/MuYunyun/blog/blob/master/React/从0到1实现React/16.RenderProps.md) 是当下类组件中复用逻辑常用的手段。 |
14 | 14 |
|
15 | | - |
| 15 | +HOC: |
16 | 16 |
|
17 | | -* 性能, 需要额外的组件实例存在额外的开销; (hoc、render props) |
18 | | -* 命名重复性, 在一个组件中同时使用多个 hoc, 不排除这些 hoc 里的方法存在命名冲突的问题; (hoc) |
| 17 | +```js |
| 18 | +// react-redux |
| 19 | +class MyComponent extends React.Component {} |
| 20 | +export default connect(mapStateToProps, mapDispatchToProps)(MyComponent) |
| 21 | +``` |
| 22 | + |
| 23 | +Render Props: |
| 24 | + |
| 25 | +```js |
| 26 | +// Beast 组件 Matrix |
| 27 | +<Matrix dataSources={dataSources}> |
| 28 | + {({ src, index }) => { |
| 29 | + return ( |
| 30 | + <> |
| 31 | + <div>name: {src.name}</div> |
| 32 | + <div>index: {index}</div> |
| 33 | + </> |
| 34 | + ) |
| 35 | + }} |
| 36 | +</Matrix> |
| 37 | +``` |
| 38 | + |
| 39 | +这两种模式是否存在缺陷呢? |
19 | 40 |
|
20 | | -二: `单个组件中的逻辑复用`: Class 中的生命周期 `componentDidMount`、`componentDidUpdate` 甚至 `componentWillUnMount` 中的大多数逻辑基本是类似的, 必须拆散在不同生命周期中维护相同的逻辑对使用者是不友好的, 这样也造成了组件的代码量增加。 |
| 41 | +* 开发层面 |
| 42 | + * 排查问题不易。当嵌套层级过多后, 数据源的追溯会变得十分困难, 导致排查定位问题不易; (Hoc、Render props) |
| 43 | + *  |
| 44 | + * 业务逻辑分散。基于`生命周期编程`, 代码量增加。 |
| 45 | + * 类组件生命周期 `componentDidMount`、`componentDidUpdate`、`componentWillUnMount` 中大多数逻辑是类似的, 拆散在不同生命周期中维护相同的逻辑对使用者是不友好, 同时也造成了组件的代码量增加。 |
| 46 | + * 基于类:  |
| 47 | + * 基于 Hooks:  |
| 48 | + * 属性覆盖。若同时使用多个 Hoc, 容易存在命名冲突导致属性被覆盖的情况; (Hoc) |
| 49 | +* 性能开销大。组件实例化存在额外的开销; (Hoc、Render props) |
21 | 50 |
|
22 | | - |
| 51 | +此外类组件还有一些其它问题: |
23 | 52 |
|
24 | | - |
| 53 | +* 冗余的样板代码。比如 `this.xxxFn = this.xxxFn.bind(this)` |
| 54 | +* 学习成本相对高。比如要掌握各个生命周期 |
| 55 | +* 编译优化方面不理想。编译时间长, 编译出的代码体积大。另外可以见 [Vue: Update: the Class API proposal is being dropped.](https://github.com/vuejs/rfcs/pull/17#issuecomment-494242121) 这个 issue. |
25 | 56 |
|
26 | | -三: Class 的其它一些问题: 在 React 使用 Class 需要书写大量样板, 用户通常会对 Class 中 Constructor 的 bind 以及 this 的使用感到困惑; 当结合 class 与 TypeScript 一起使用时, 需要对 defaultValue 做额外声明处理; 此外 React Team 表示 Class 在机器编译优化方面也不是很理想。 |
| 57 | +* React Hooks 的常见陷阱 |
| 58 | + * 闭包陷阱, (useInterval, useFetch) |
27 | 59 |
|
28 | 60 | ### React Hooks 使用中的问题 |
29 | 61 |
|
30 | | -#### 闭包陷阱 |
| 62 | +#### 百思不解, 必是闭包 |
31 | 63 |
|
32 | 64 | * demo1: 闭包陷阱1。 [Demo 地址](https://codesandbox.io/s/22y21468r) |
33 | 65 |
|
@@ -90,55 +122,11 @@ function Demo() { |
90 | 122 | } |
91 | 123 | ``` |
92 | 124 |
|
93 | | -### React Hooks 内部探究 |
94 | | - |
95 | | -以 `useState` 和 `useReducer` 为例 |
96 | | - |
97 | | -#### 使用 useState 实现 useReducer |
98 | | - |
99 | | -```js |
100 | | -import * as React from 'react' |
101 | | -const { useState, useRef, useCallback } = React |
102 | | - |
103 | | -function useReducer(reducer, initialState) { |
104 | | - const [state, setState] = useState(initialState) |
105 | | - const reducerRef = useRef(reducer) |
106 | | - const stateRef = useRef(state) |
107 | | - |
108 | | - const dispatch = useCallback((action) => { |
109 | | - setState(reducerRef.current(stateRef.current, action)) |
110 | | - }, []) |
111 | | - |
112 | | - useEffect(() => { |
113 | | - reducerRef.current = reducer |
114 | | - }, [reducer]) |
115 | | - |
116 | | - useEffect(() => { |
117 | | - stateRef.current = state |
118 | | - }, [state]) |
119 | | - |
120 | | - return [state, dispatch] |
121 | | -} |
122 | | -``` |
123 | | - |
124 | | -#### 使用 useReducer 实现 useState |
125 | | - |
126 | | -```js |
127 | | -import * as React from 'react' |
128 | | -const { useReducer, useCallback } = React |
| 125 | +#### useSetState |
129 | 126 |
|
130 | | -function useState(initialState) { |
131 | | - const [state, dispatch] = useReducer((state, action) => { |
132 | | - return action |
133 | | - }, initialState) |
| 127 | +#### 规则陷阱 |
134 | 128 |
|
135 | | - const setState = useCallback( |
136 | | - (newState) => dispatch(newState), [] |
137 | | - ) |
138 | | - |
139 | | - return [state, setState] |
140 | | -} |
141 | | -``` |
| 129 | +> eslint-hooks 插件 |
142 | 130 |
|
143 | 131 | ### 相关链接 |
144 | 132 |
|
|
0 commit comments