Skip to content

build(deps): bump handlebars from 4.1.2 to 4.5.3 #2

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

Closed
wants to merge 2 commits into from
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
147 changes: 146 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,146 @@
# ReactHooksTraining
Hooks 新人培训演讲大纲

### React Logo 与 Hooks

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

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

### why Hooks?

一: `多个组件间逻辑复用`: 在 Class 中使用 React 不能将带有 state 的逻辑给单独抽离成 function, 其只能通过嵌套组件的方式来解决多个组件间逻辑复用的问题, 基于嵌套组件的思想存在 [HOC](https://github.com/MuYunyun/blog/blob/master/React/从0到1实现React/8.HOC探索.md) 与 `render props` 两种设计模式。但是这两种设计模式是否存在缺陷呢?

* 嵌套地狱, 当嵌套层级过多后, 数据源的追溯会变得十分困难, 导致定位 bug 不容易; (hoc、render props)

![](http://with.muyunyun.cn/b9147e8bd39e7badccc3190fb473755f.jpg)

* 性能, 需要额外的组件实例存在额外的开销; (hoc、render props)
* 命名重复性, 在一个组件中同时使用多个 hoc, 不排除这些 hoc 里的方法存在命名冲突的问题; (hoc)

二: `单个组件中的逻辑复用`: Class 中的生命周期 `componentDidMount`、`componentDidUpdate` 甚至 `componentWillUnMount` 中的大多数逻辑基本是类似的, 必须拆散在不同生命周期中维护相同的逻辑对使用者是不友好的, 这样也造成了组件的代码量增加。

![](http://with.muyunyun.cn/0c94989b2eced65c368ff2389464fd0a.jpg-400)

![](http://with.muyunyun.cn/d21d7974dbec9a49603e2211b354496c.jpg-400)

三: Class 的其它一些问题: 在 React 使用 Class 需要书写大量样板, 用户通常会对 Class 中 Constructor 的 bind 以及 this 的使用感到困惑; 当结合 class 与 TypeScript 一起使用时, 需要对 defaultValue 做额外声明处理; 此外 React Team 表示 Class 在机器编译优化方面也不是很理想。

### React Hooks 使用中的问题

#### 闭包陷阱

* demo1: 闭包陷阱1。 [Demo 地址](https://codesandbox.io/s/22y21468r)

1. Hooks/class demo 对比演示;
2. Hooks/class 互相切换为对方的形态;

结论: 问题不在于是使用 Hooks 还是 class, 本质是受到闭包的影响。

* demo2: 闭包陷阱2

由 Class 转换过来的用户习惯 `setCount(count + 1))` 的方式。但在 Hooks 中这样子使用会产生闭包问题导致 `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>
)
}
```

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

引出为此提供 `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 内部探究

以 `useState` 和 `useReducer` 为例

#### 使用 useState 实现 useReducer

```js
import * as React from 'react'
const { useState, useRef, useCallback } = React

function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState)
const reducerRef = useRef(reducer)
const stateRef = useRef(state)

const dispatch = useCallback((action) => {
setState(reducerRef.current(stateRef.current, action))
}, [])

useEffect(() => {
reducerRef.current = reducer
}, [reducer])

useEffect(() => {
stateRef.current = state
}, [state])

return [state, dispatch]
}
```

#### 使用 useReducer 实现 useState

```js
import * as React from 'react'
const { useReducer, useCallback } = React

function useState(initialState) {
const [state, dispatch] = useReducer((state, action) => {
return action
}, initialState)

const setState = useCallback(
(newState) => dispatch(newState), []
)

return [state, setState]
}
```

### 相关链接

* [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)
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"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": {
"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>
87 changes: 87 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState, useRef, useEffect, useReducer } from "react"
import ReactDOM from "react-dom"
import useInterval from './useInterval'

import "./styles.css"

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>
)
}

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>
);
}
}

/* --------------- 分割线 --------------- */

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

useEffect(() => {
const id = setInterval(() => {
setCount(count + 1)
}, 2000)

return () => {
clearInterval(id)
}
}, [])

return <div>Count: {count}</div>
}

function View() {
return (
<React.Fragment>
<HookApp />
<hr />
<ClassApp />
<hr />
{/* <Demo /> */}
</React.Fragment>
);
}

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;
}
15 changes: 15 additions & 0 deletions src/useInterval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { useState, useRef, useEffect } from "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])
}
Loading