Open brickspert opened 5 years ago
为什么 state声明在外面,点击的时候,view才变化,没想明白 @brickspert
@liang520 如果放在 useState 里面,是不是重新渲染每次都会重新调用 useState 重置掉?
@brickspert 看明白了
memoizedState => memorizedState
砖家写的文章依旧简单明了~
很清晰明了了
除了用变量存state还有别的吗
当我以为我会用hook的时候 useEffect 内的 setInterval 把我打蒙了,闭包是啥时候出现的都没看懂
一直关注,一直学习,感谢你的陪伴,可以借鉴学习React Hooks 入门教程http://www.ruanyifeng.com/blog/2019/09/react-hooks.html
follow
赞,会用与会实现,理解的深度就是不一样
为什么 state声明在外面,点击的时候,view才变化,没想明白 @brickspert
不然的话state只是一个useState函数里边的局部变量,每次执行都会重新初始化为0呀
是否需要在setState函数中把cursor重置为0,在rerender的时候才能按照hook在组件函数内的书写顺序从0依次获取memoizedState中的值?
function setState(newState) {
memoizedState[currentCursor] = newState;
// reset
cursor = 0;
render();
}
是否需要在setState函数中把cursor重置为0,在rerender的时候才能按照hook在组件函数内的书写顺序从0依次获取memoizedState中的值?
function setState(newState) { memoizedState[currentCursor] = newState; // reset cursor = 0; render(); }
例子中在 render() 里面把 cursor 置为 0 了。
这个事件触发的时候应该下标应该是 1 吧
这个事件触发的时候应该下标应该是 1 吧
我也觉得此时的cursor不应该为0,为1的是currentCursor 这个局部变量,用来获取userName这个state在数组中的位置
这个事件触发的时候应该下标应该是 1 吧
我也觉得此时的cursor不应该为0,为1的是currentCursor 这个局部变量,用来获取userName这个state在数组中的位置
同意。随处可见的闭包啊~
这个事件触发的时候应该下标应该是 1 吧
为什么是 1 呢?每次都是从 0 开始重新执行的。可以在这个例子中试试呢: https://codesandbox.io/s/50ww35vkzl
请问,这个useState在多个组件中引入,彼此之间会不会有影响(全局只有一个memoizedState )?
请问,这个useState在多个组件中引入,彼此之间会不会有影响(全局只有一个memoizedState )?
这个例子里面会。因为共用了一个 memoizedState。 但是在 React 中,是把数据存在 fiber node 上的。也就是,每个组件都有自己的 memoizedState.
@brickspert 多谢多谢
棒! _state = _state | initialValue
应该是 _state = _state || initialValue
棒!
_state = _state | initialValue
应该是_state = _state || initialValue
感谢~,已修正。
这个事件触发的时候应该下标应该是 1 吧
为什么是 1 呢?每次都是从 0 开始重新执行的。可以在这个例子中试试呢: https://codesandbox.io/s/50ww35vkzl
我看你代码是 render 的时候才修改 cursor 为 0 吧,事件触发的时候,当时的 currentCursor 是 1,这时就会改变 memorizState[1] 的值
memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的?
我们知道,react 会生成一棵组件树(或Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,伴随组件一起出生,一起死亡。
这句话我不能理解,博主能引用源代码来证明么? 就我理解,函数式组件既然已经是stateless,就不会有所谓出生,死亡等生命周期了。我更倾向于react的hook信息,即整个hook的链表是一个全局变量。
memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的? 我们知道,react 会生成一棵组件树(或Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,伴随组件一起出生,一起死亡。
这句话我不能理解,博主能引用源代码来证明么? 就我理解,函数式组件既然已经是stateless,就不会有所谓出生,死亡等生命周期了。我更倾向于react的hook信息,即整个hook的链表是一个全局变量。
收回我的猜想。全局的hook无法实现条件渲染,应该是每个组件对应各自的hook。
还是无法理解: “Capture Value” 特性是如何产生的
还是无法理解: “Capture Value” 特性是如何产生的
每次rerender都会重新执行一遍函数组件本身, 每次执行都会是全新的context,capture只是闭包保存对应的context,所以后续更新 不会影响之前的context,那之前闭包捕获的context也就不会变了。
理解了, 每次render 就是一个闭包, 会缓存住当前的所有状态, react class 是直接更新this.props的指向, 且没有闭包的概念, 所以全变了
在demo3中,没有调用诸如useState或者this.setState之类的方法,那react是如何知道并且触发更新的呢?
在demo3中,没有调用诸如useState或者this.setState之类的方法,那react是如何知道并且触发更新的呢?
抱歉,忽略了里面的render方法
function setState(newState) {
memoizedState[currentCursor] = newState;
cursor = currentCursor; //是不是少了这一句??
render();
}
function setState(newState) { memoizedState[currentCursor] = newState; cursor = currentCursor; //是不是少了这一句?? render(); }
不需要吧 下一步render()就會把cursor = 0 並且再次依照順序調用所有函數組件
大神 有一点想请教一下 function useState(initialValue) { memoizedState[cursor] = memoizedState[cursor] || initialValue; const currentCursor = cursor; function setState(newState) { memoizedState[currentCursor] = newState; render(); } return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1 } 对于memoizedState[cursor] = memoizedState[cursor] || initialValue;这么实现的话 是不是如果setState(null)或者是setState(undefind)是不是 state都会被付成默认值了
大神 有一点想请教一下 function useState(initialValue) { memoizedState[cursor] = memoizedState[cursor] || initialValue; const currentCursor = cursor; function setState(newState) { memoizedState[currentCursor] = newState; render(); } return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1 } 对于memoizedState[cursor] = memoizedState[cursor] || initialValue;这么实现的话 是不是如果setState(null)或者是setState(undefind)是不是 state都会被付成默认值了
我也是有这么个疑问,如果值本身就是false的话,怎么办?
大神 有一点想请教一下 function useState(initialValue) { memoizedState[cursor] = memoizedState[cursor] || initialValue; const currentCursor = cursor; function setState(newState) { memoizedState[currentCursor] = newState; render(); } return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1 } 对于memoizedState[cursor] = memoizedState[cursor] || initialValue;这么实现的话 是不是如果setState(null)或者是setState(undefind)是不是 state都会被付成默认值了
确实会有这个问题。 这个文章只是一个示例 demo,不是百分百严谨。
大神 有一点想请教一下 function useState(initialValue) { memoizedState[cursor] = memoizedState[cursor] || initialValue; const currentCursor = cursor; function setState(newState) { memoizedState[currentCursor] = newState; render(); } return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1 } 对于memoizedState[cursor] = memoizedState[cursor] || initialValue;这么实现的话 是不是如果setState(null)或者是setState(undefind)是不是 state都会被付成默认值了
确实会有这个问题。 这个文章只是一个示例 demo,不是百分百严谨。
明白啦!还是感谢作者带来质量那么高的文章。
这个事件触发的时候应该下标应该是 1 吧
为什么是 1 呢?每次都是从 0 开始重新执行的。可以在这个例子中试试呢: https://codesandbox.io/s/50ww35vkzl
这里从0开始,是指render都会从0开始执行hook。但是对于setUsername('fan hello')这个setState方法来说,cursor应该是1。 这是我个人的理解,这个图上标注似乎会引起一点歧义,如果不对请指正
这个事件触发的时候应该下标应该是 1 吧
为什么是 1 呢?每次都是从 0 开始重新执行的。可以在这个例子中试试呢: https://codesandbox.io/s/50ww35vkzl
这里从0开始,是指render都会从0开始执行hook。但是对于setUsername('fan hello')这个setState方法来说,cursor应该是1。 这是我个人的理解,这个图上标注似乎会引起一点歧义,如果不对请指正
第一句描述的没有异议,第二句中的 "cursor 应该是 1" 这句存在歧义,我们可以观察一下 setState 方法执行过程,发现其中维护memoizedState 数组下标的变量是 useState 方法中定义的 currentCursor 变量而不是 cursor,实际上 currentCursor 的值才是1。变量 cursor 的值在执行setState 中 render 方法之前是 memoizedState 数组的长度(即所存储hooks的数量,因为 useState 和 useEffect 中最后都执行了cursor++ 操作),在执行setState 中 render方法之后才会重置为 0。 图中描述的是变量 cursor 值的变化,所以作者描述的没什么问题,如果加上 currentCursor 值得变化大家可能就更容易理解了。
看了好久那里没想明白cursor怎么重置为0的,看了demo才知道,哭了。
🧱家就是强 通俗易懂 顺便问个问题 React为何要使用链表串联多个hook 相比数组的优势是什么呢?
🧱家就是强 通俗易懂 顺便问个问题 React为何要使用链表串联多个hook 相比数组的优势是什么呢?
感觉链表的灵活性会比数组来的高吧?
🧱家就是强 通俗易懂 顺便问个问题 React为何要使用链表串联多个hook 相比数组的优势是什么呢?
数组存储区间需要连续的内存,链表是则是非连续、非顺序的存储结构
不懂就问,render函数怎么来的,是如何触发函数组件跟新的呢?
不懂就问,render函数怎么来的,是如何触发函数组件跟新的呢?
看 demo 代码,有定义 render 函数
最开始有个疑问:为什么 useState 的 memoizedState 要是数组?如果是对象多好,这样就能一一对应上了,就可以突破 hooks 只能写在函数组件顶层的限制了。
看到最后,发现其实是个链表,并不是简单的数组呀。。。 所以我猜测为什么 memoizedState 不用 key value 这种方式存的原因是出于性能考虑吗?(也就是 key value 形式的数据结构 并没有 链表 形式的快)
还有建议:给 useState 里的 render() 那块加个注释,我刚开始看这块的时候,也比较懵
最开始有个疑问:为什么 useState 的 memoizedState 要是数组?如果是对象多好,这样就能一一对应上了,就可以突破 hooks 只能写在函数组件顶层的限制了。
看到最后,发现其实是个链表,并不是简单的数组呀。。。 所以我猜测为什么 memoizedState 不用 key value 这种方式存的原因是出于性能考虑吗?(也就是 key value 形式的数据结构 并没有 链表 形式的快)
最近面试遇到这个问题,大致了解了一下,链表是在内存里申请了一块连续的空间,而map是随机分散存储的,所以链表的查询会更快一些,一般大佬们的程序都会考虑要结合硬件层面来设计的
受教了
最开始有个疑问:为什么 useState 的 memoizedState 要是数组?如果是对象多好,这样就能一一对应上了,就可以突破 hooks 只能写在函数组件顶层的限制了。 看到最后,发现其实是个链表,并不是简单的数组呀。。。 所以我猜测为什么 memoizedState 不用 key value 这种方式存的原因是出于性能考虑吗?(也就是 key value 形式的数据结构 并没有 链表 形式的快)
最近面试遇到这个问题,大致了解了一下,链表是在内存里申请了一块连续的空间,而map是随机分散存储的,所以链表的查询会更快一些,一般大佬们的程序都会考虑要结合硬件层面来设计的
链表是动态分配内存,是非连续的。数组才是连续的,且是静态分配的
前言
目前,Hooks 应该是 React 中最火的概念了,在阅读这篇文章之前,希望你已经了解了基本的 Hooks 用法。
在使用 Hooks 的时候,我们可能会有很多疑惑
这篇文章我们不会讲解 Hooks 的概念和用法,而是会带你从零实现一个 tiny hooks,知其然知其所以然。
useState
最简单的 useState 用法是这样的:
demo1: https://codesandbox.io/s/v0nqm309q3
基于 useState 的用法,我们尝试着自己实现一个 useState:
demo2:https://codesandbox.io/s/myy5qvoxpp
这时我们发现,点击 Button 的时候,count 并不会变化,为什么呢?我们没有存储 state,每次渲染 Counter 组件的时候,state 都是新重置的。
自然我们就能想到,把 state 提取出来,存在 useState 外面。
demo3:https://codesandbox.io/s/q9wq6w5k3w
到目前为止,我们实现了一个可以工作的 useState,至少现在来看没啥问题。
接下来,让我们看看 useEffect 是怎么实现的。
useEffect
useEffect 是另外一个基础的 Hook,用来处理副作用,最简单的用法是这样的:
demo4:https://codesandbox.io/s/93jp55qyp4
我们知道 useEffect 有几个特点:
我们来实现一个 useEffect
demo5:https://codesandbox.io/s/3kv3zlvzl1
到这里,我们又实现了一个可以工作的 useEffect,似乎没有那么难。
此时我们应该可以解答一个问题:
Q:为什么第二个参数是空数组,相当于
componentDidMount
?A:因为依赖一直不变化,callback 不会二次执行。
Not Magic, just Arrays
到现在为止,我们已经实现了可以工作的 useState 和 useEffect。但是有一个很大的问题:它俩都只能使用一次,因为只有一个 _state 和 一个 _deps。比如
count 和 username 永远是相等的,因为他们共用了一个 _state,并没有地方能分别存储两个值。我们需要可以存储多个 _state 和 _deps。
如 《React hooks: not magic, just arrays》所写,我们可以使用数组,来解决 Hooks 的复用问题。
demo6:https://codesandbox.io/s/50ww35vkzl
代码关键在于:
我们用图来描述 memoizedState 及 cursor 变化的过程。
1. 初始化
2. 初次渲染
3. 事件触发
4. Re Render
到这里,我们实现了一个可以任意复用的 useState 和 useEffect。
同时,也可以解答几个问题:
Q:为什么只能在函数最外层调用 Hook?为什么不要在循环、条件判断或者子函数中调用。
A:memoizedState 数组是按 hook定义的顺序来放置数据的,如果 hook 顺序变化,memoizedState 并不会感知到。
Q:自定义的 Hook 是如何影响使用它的函数组件的?
A:共享同一个 memoizedState,共享同一个顺序。
Q:“Capture Value” 特性是如何产生的?
A:每一次 ReRender 的时候,都是重新去执行函数组件了,对于之前已经执行过的函数组件,并不会做任何操作。
真正的 React 实现
虽然我们用数组基本实现了一个可用的 Hooks,了解了 Hooks 的原理,但在 React 中,实现方式却有一些差异的。
React 中是通过类似单链表的形式来代替数组的。通过 next 按顺序串联所有的 hook。
memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的?
我们知道,react 会生成一棵组件树(或Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,伴随组件一起出生,一起死亡。
参考文章
❤️感谢大家
关注公众号「前端技术砖家」,拉你进交流群,大家一起共同交流和进步。