forked from wuyawei/fe-code
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hooks.html
105 lines (105 loc) · 4.04 KB
/
hooks.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="name"></div>
<button class="add">add</button>
<span id="count"></span>
</body>
<!-- https://www.netlify.com/blog/2019/03/11/deep-dive-how-do-react-hooks-really-work/ -->
<!-- https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/ -->
<!--https://overreacted.io/zh-hans/react-as-a-ui-runtime/-->
<!-- https://github.com/brickspert/blog/issues/26 -->
<!-- https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e -->
<!-- https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberHooks.js -->
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
// useState/useEffect/useRef/useCallback/useReducer
let HOOKS = [];
let currentIndex = 0;
function useState(initialState) {
HOOKS[currentIndex] = HOOKS[currentIndex] || (typeof initialState === 'function' ? initialState() : initialState);
const memoryCurrentIndex = currentIndex; // currentIndex 是全局可变的,需要保存本次的
const setState = p => {
let newState = p;
const update = () => {
if (typeof p === 'function') {
newState = p(HOOKS[memoryCurrentIndex]);
}
HOOKS[memoryCurrentIndex] = newState;
}
Tick.push(update);
Tick.nextTick();
};
return [HOOKS[currentIndex++], setState];
}
function useEffect(fn, deps) {
const effect = HOOKS[currentIndex];
const _deps = effect && effect._deps;
const hasChange = _deps ? !deps.every((v, i) => _deps[i] === v) : true;
const memoryCurrentIndex = currentIndex; // currentIndex 是全局可变的
if (!deps || hasChange) {
const _effect = effect && effect._effect;
setTimeout(() => {
typeof _effect === 'function' && _effect(); // 每次先判断一下有没有上一次的副作用需要卸载
const ef = fn();
HOOKS[memoryCurrentIndex] = {...HOOKS[memoryCurrentIndex], _effect: ef}; // 更新effects
})
}
HOOKS[currentIndex++] = {_deps: deps, _effect: null};
}
const Tick = {
queue: [],
push: function(task) {
this.queue.push(task);
},
nextTick: function() {
setTimeout(() => {
// console.log(this.queue);
if (this.queue.length) { // 一次渲染后,全部出栈,确保单次事件循环不会重复渲染
this.queue.forEach(f => f()); // 依次执行队列中所有任务
currentIndex = 0; // 重置计数
this.queue = []; // 清空队列
render();
}
})
}
};
function render() {
const [count, setCount] = useState(0);
useEffect(() => {
// 清除副作用、支持回调
const time = setInterval(() => {
setCount(count + 1);
setName(name + 'ha');
}, 1000)
return () => {
clearInterval(time);
}
}, [count]);
const [name, setName] = useState(() => 'hi');
// $('.add').on('click', () => { // 重新绑定需要解绑之前的
// setCount(count + 1);
// })
// console.log(666);
$('.add')[0].onclick = () => { // 重复绑定会覆盖之前的
// 现在触发多次set,不会重渲染多次
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
setName(name + 'ha');
};
$('#count').html(count);
$('.name').html(name);
}
render();
</script>
</html>