Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(deps): bump acorn from 5.7.3 to 5.7.4 #3

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"extends": [
"airbnb",
"plugin:jsx-a11y/recommended",
"prettier",
"prettier/react"
],
"plugins": [
"jsx-a11y",
"prettier",
// "react-hooks"
],
"rules": {
// "react-hooks/rules-of-hooks": 2, // Checks rules of Hooks
// "react-hooks/exhaustive-deps": 1, // Checks effect dependencies
"semi": 0,
"react/jsx-filename-extension": [
1,
{
"extensions": [
".js",
".jsx"
]
}
],
"prettier/prettier": [
"error",
{
"semi": false
}
]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# .npmrc
registry=http://r.cnpmjs.org/
3 changes: 3 additions & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# .yarnrc

registry "http://r.cnpmjs.org/"
131 changes: 130 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,130 @@
# ReactHooksTraining
Hooks 新人培训演讲大纲

### Hooks

![](http://with.muyunyun.cn/ddbdcec2fc39ba350fc74647f4fad6f5.jpg-300)

React 的 logo 是一个原子图案, 原子组成了物质。类似的, React 就如原子般构成了页面的表现; 而 Hooks 就如夸克, 其更接近 React 本质的样子, 但是直到 2019 年才被真正设计出来(花了近 4 年时间)。 —— Dan in React Conf(2018)

### React 中的逻辑复用

在 Hooks 出来之前, 类组件是如何进行逻辑复用的呢?

熟悉 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) 是当下类组件中复用逻辑常用的手段。

HOC:

```js
// react-redux
class MyComponent extends React.Component {}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
```

Render Props:

```js
// Beast 组件 Matrix
<Matrix dataSources={dataSources}>
{({ src, index }) => {
return (
<>
<div>name: {src.name}</div>
<div>index: {index}</div>
</>
)
}}
</Matrix>
```

这两种模式是否存在缺陷呢?

* 开发层面
* 排查问题不易。当嵌套层级过多后, 数据源的追溯会变得十分困难, 导致排查定位问题不易; (Hoc、Render props)
* ![](http://with.muyunyun.cn/b9147e8bd39e7badccc3190fb473755f.jpg)
* 业务逻辑分散。基于`生命周期编程`, 代码量增加。
* 类组件生命周期 `componentDidMount`、`componentDidUpdate`、`componentWillUnMount` 中大多数逻辑是类似的, 拆散在不同生命周期中维护相同的逻辑对使用者是不友好, 同时也造成了组件的代码量增加。
* 基于类: ![](http://with.muyunyun.cn/0c94989b2eced65c368ff2389464fd0a.jpg-400)
* 基于 Hooks: ![](http://with.muyunyun.cn/d21d7974dbec9a49603e2211b354496c.jpg-400)
* 属性覆盖。若同时使用多个 Hoc, 容易存在命名冲突导致属性被覆盖的情况; (Hoc)
* 性能开销大。组件实例化存在额外的开销; (Hoc、Render props)

此外类组件还有一些其它问题:

* 冗余的样板代码。比如 `this.xxxFn = this.xxxFn.bind(this)`
* 学习成本相对高。比如要掌握各个生命周期
* 编译优化方面不理想。编译时间长, 编译出的代码体积大。另外可以见 [Vue: Update: the Class API proposal is being dropped.](https://github.com/vuejs/rfcs/pull/17#issuecomment-494242121) 这个 issue.

* React Hooks 的常见陷阱
* 闭包陷阱, (useInterval, useFetch)

### React Hooks 使用中的问题

#### 百思不解, 必是闭包

* Part1: 闭包陷阱

> [Demo 地址](https://codesandbox.io/s/22y21468r)

1. 函数组件/类组件 Demo 对比演示;
2. 函数组件/类组件互相切换为对方的形态;

结论: 问题不在于是使用函数组件还是类组件, 本质是受到闭包的影响。

* Part2: 闭包陷阱2

之前写过类组件的用户应该习惯 `setCount(count + 1))` 的方式。但在函数组件中这样子使用会产生闭包问题导致 `count` 不会增加。

```js
function Demo() {
const [count, setCount] = React.useState(0)
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1)
}, 2000)

return () => {
clearInterval(id)
}
}, [])
return (
<div>Count: {count}</div>
)
}
```

提供若干种解法。用户说还是想用 `setCount(count + 1)` 的形式怎么办

引出自定义 hooks 的概念。介绍 `useInterval` 钩子, 顺利过渡到 `beast-hooks`

```js
function useInterval(callback, delay: number) {
const cbRef = useRef({})
useEffect(() => {
cbRef.current = callback
}, [callback])
useEffect(() => {
setInterval(() => {
cbRef.current()
}, delay)
}, [delay])
}
```

用法:

```js
function Demo() {
const [count, setCount] = React.useState(0)

useInterval(() => {
setCount(count + 1)
}, 2000)

return (<div>Count: {count}</div>)
}
```

### 相关链接

* [React Hooks 深入系列](https://github.com/MuYunyun/blog/blob/master/React/React_Hooks深入系列.md)
* [React Hooks 设计模式](https://github.com/MuYunyun/blog/blob/master/React/React_Hooks设计模式.md)
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "new",
"version": "1.0.0",
"description": "",
"keywords": [],
"main": "src/index.js",
"dependencies": {
"react": "16.8.6",
"react-dom": "16.8.6",
"react-scripts": "2.1.8"
},
"devDependencies": {
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.4",
"eslint-plugin-react-hooks": "^4.0.8",
"prettier": "^2.0.5",
"typescript": "3.3.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
43 changes: 43 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>

<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>

</html>
25 changes: 25 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable no-unused-vars */
import React from "react"
import ReactDOM from "react-dom"
// eslint-disable-next-line import/named
import { HookApp, ClassApp } from "./tasks/task1"
import Demo from "./tasks/task2"
import RuleCase from "./tasks/task3_if_rule"
import "./styles.css"

function View() {
return (
<>
{/* <HookApp />
<hr />
<ClassApp />
<hr /> */}
{/* <Demo /> */}
<RuleCase />
</>
)
}

// eslint-disable-next-line no-undef
const rootElement = document.getElementById("root")
ReactDOM.render(<View />, rootElement)
9 changes: 9 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.App {
font-family: sans-serif;
text-align: center;
}

button {
padding: 10px 20px;
margin: 10px;
}
4 changes: 4 additions & 0 deletions src/tasks/hooks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import useInterval from "./useInterval"
import useForceUpdate from "./useForceUpdate"

export { useInterval, useForceUpdate }
9 changes: 9 additions & 0 deletions src/tasks/hooks/useForceUpdate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from "react"

const { useReducer } = React

function useForceUpdate() {
return useReducer((n) => n + 1, 0)[1]
}

export default useForceUpdate
17 changes: 17 additions & 0 deletions src/tasks/hooks/useInterval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react"

const { useRef, useEffect } = React

export default function useInterval(callback, delay) {
const cbRef = useRef({})
useEffect(() => {
cbRef.current = callback
}, [callback])
useEffect(() => {
const id = setInterval(() => {
cbRef.current()
}, delay)

return () => clearInterval(id)
}, [delay])
}
52 changes: 52 additions & 0 deletions src/tasks/task1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from "react"

const { useState } = React

export function HookApp() {
const [count, setCount] = useState(0)
const handleAdd = () => {
setCount(count + 1)
}
const handleAlert = () => {
setTimeout(() => {
window.alert(count)
}, 2000)
}
return (
<div className="App">
<h1>Hook</h1>
<h1>{count}</h1>
<button onClick={handleAdd}>add</button>
<button onClick={handleAlert}>alert</button>
</div>
)
}

export class ClassApp extends React.Component {
state = {
count: 0
};

handleAdd = () => {
this.setState({
count: this.state.count + 1
});
};

handleAlert = () => {
setTimeout(() => {
window.alert(this.state.count);
}, 2000);
};

render() {
return (
<div className="App">
<h1>Class</h1>
<h1>{this.state.count}</h1>
<button onClick={this.handleAdd}>add</button>
<button onClick={this.handleAlert}>alert</button>
</div>
);
}
}
Loading