代码写完就忘了。下面挑一个最印象深刻的点来简单说说。快照思想
useState中的快照
处理各个fiber任务的时候,碰到了函数类型的fiber,会通过一个全局变量wipFiber(work in progress fiber)给保存起来。useState返回的更新函数updater要如何拿到当前的函数fiber呢?如果直接引用wipFiber,那调用updater的时候,引用永远都是最后的那个函数fiber了。所以要在内部保存一个局部变量。
这个变量的意义就是在处理到当前函数组件的那个时刻,把过去的fiber保存下来。render是一个巨大的流程,而这个变量可以把某个阶段的状态保存住。就像是一个电影,而在某一帧处拍了一张照片。这是我第一次从mini-react中感受到快照这个概念。
function useState(initialValue) { const currentFiber = wipFiber // ... function setHandler(action) { // ... wipRoot = { ...currentFiber, // 重点在这里 alternate: currentFiber } nextUnitOfWork = wipRoot } return [stateHook.state, setHandler] }
React.createElement也是一个快照
function App() { const [count, setCount] = React.useState(10) const [name, setName] = React.useState('wyq') function handleClick() { setCount(c => c + 1) setName(n => n + ' -love') } React.useEffect(() => { console.log('count useEffect') return () => { console.log('count cleanup') } }, [count]) return ( <div> <h1>App</h1> <h2>{count}</h2> <h2>{name}</h2> <button onClick={handleClick}>+1</button> </div> ) }
在刚写到从根节点root更新节点的时候,对使用currentRoot.alternate.props.children还有疑问。我想两次都使用相同的[el],最后生成的真实dom不也是一样的吗?怎么可能更新呢?
但是实际处理时,函数组件就是和普通的函数一样,而且是纯函数。规定了props输入,它return 输出的内容一定是只有唯一一种可能。函数组件里再使用别的函数组件也同理。当你修改了某些状态,触发的rerender,就相当于触发了一大堆React.createElement()的调用,调用时入参改变了,那createElement返回值自然就改变了。
每一次render后生成的dom也像是一张快照,就是静态的,只有触发了rerender,才会拍下一张照片。
这种快照思想在工作中基本用不到。因为只适合在有整个大流程中保存某时刻的片段才适合用。平时用仅仅是函数中去引用外部函数。流程的循环调用可能只有设计框架才适合吧。