Build your own React_8 Hooks
Posted 一只前端小马甲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_8 Hooks相关的知识,希望对你有一定的参考价值。
前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。
我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。
假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。
你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。
从零开始重写React框架,我们需要遵循以下步骤:
步骤八:Hooks
最后一步。我们了解了函数组件,现在给函数组件增加state。
//const Didact =
// createElement,
// render,
//
/** @jsx Didact.createElement */
function App(props)
return <h1>H1 props.name</h1>
const element = <App name="foo" />
const container = document.getElementById("root")
Didact.render(element, container)
现在把我们的组件变成经典的计数器组件。我当我们点击一下,计数器的state值加一。
//const Didact =
// createElement,
// render,
useState,
//
/** @jsx Didact.createElement */
function Counter()
const [state, setState] = Didact.useState(1)
return (
<h1 onClick=()=>setState(c=>c+1)>
Count: state
</h1>
)
const element = <Counter />
//const container = document.getElementById("root")
//Didact.render(element, container)
注意我们在这里用Didact.useState获取和更新计数器的值。
下面是我们调用Counter函数的地方,我们同样在里面调用useState函数。
// return nextFiber.sibling
//
// nextFiber = nextFiber.parent
//
//
function updateFunctionComponent(fiber)
const children = [fiber.type(fiber.props)]
// reconcileChildren(fiber, children)
function useState(initial)
// TODO
//function updateHostComponent(fiber)
// if(!fiber.dom)
// fiber.dom = createDom(fiber)
//
// reconcileChildren(fiber, fiber.props, children)
在调用函数组件时,我们需要初始化一些全局变量,以便于我们在useState中使用。
首先,我们需要设置渲染中的纤维(work in process fiber)。
我们同时需要对纤维增加一个hooks数组,用来保存当在同一个函数组件中调用多次setState的state。我们还保存着当前hook的索引。
//
// nextFiber = nextFiber.parent
//
//
let wipFiber = null
let hookIndex = null
function updateFunctionComponent(fiber)
wipFiber = fiber
hookIndex = 0
wipFiber.hooks = []
// const children = [fiber.type(fiber.props)]
// reconcileChildren(fiber, children)
//function useState(initial)
// TODO
//
当函数组件调用useState,我们检查下是否有老的hook。我们通过纤维alternate属性中的hook索引来检查。
如果我们有老的hook,当调用useState没传入初始值,我们复制老的hook的状态值,传给新的hook。
然后我们给纤维增加一个新hook,把该hook的索引加一,并返回状态值。
// reconcileChildren(fiber, children)
//
function useState(initial)
const oldHook =
wipFiber.alternate &&
wipFiber.alternate.hooks &&
wipFiber.alternate.hooks[hookIndex]
const hook =
state: oldHook ? oldHook.state : initial,
wipFiber.hooks.push(hook)
hookIndex++
return [hook.state]
//function updateHostComponent(fiber)
// if(!fiber.dom)
useState函数还要返回一个更新状态的函数,因此我们定义一个setState函数用于接收修改动作(计数器中的修改动作就是状态值加一)。
我们把修改动作入队列,该队列作为hook的一个属性。
然后我们做的事情跟在render函数中做的类似,设置一个新的渲染中的根节点,作为下一个渲染单元任务,这样我们的渲染循环就能开始一段新的渲染任务。
// wipFiber.alternate.hooks[hookIndex]
const hook =
// state: oldHook ? oldHook.state : initial,
queue: [],
const setState = action =>
hook.queue.push(action)
wipRoot =
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot,
nextUnitOfWork = wipRoot
deletions = []
// wipFiber.hooks.push(hook)
// hookIndex++
return [hook.state, setState]
但是到这里我们还没有执行修改动作。
我们将在下次渲染组件时执行上个修改动作,我们通过老hook的队列获取了所有的修改动作,然后将它们一个个赋值给新hook的state,此时返回的状态都是更新过的。
// wipFiber.alternate.hooks &&
// wipFiber.alternate.hooks[hookIndex]
// const hook =
// state: oldHook ? oldHook.state : initial,
// queue: [],
//
const actions = oldHooks ? oldHook.queue : []
actions.forEach(action =>
hook.state = action(hook.state)
)
// const setState = action =>
// hook.queue.push(action)
// wipRoot =
// dom: currentRoot.dom,
// props: currentRoot.props,
// alternate: currentRoot
//
React之旅到此结束了,我们已经构建了我们自己的React框架。
总结
步骤七,作者介绍了React函数组件的更新和删除的方法,对performUnitOfWork函数以及对应的commitWork函数做些修改。
步骤八则介绍了函数组件通过hook修改状态的具体实现,我们给纤维增加一个hook属性,保存了状态值和修改动作,每次用户产生了修改动作,会在下次函数组件渲染时执行,返回更新后的状态值。
到此我的翻译工作也告一段落,我在翻译期间不光自己对于React的理解更深了一些,还遇到了一起追求进步,探索知识的小伙伴,由衷体会到了导师信仰的佛家思想,“佛渡人也度己”。学无止尽,我们探索真理的道路也永不停止,后面我会继续在博客里面发些技术文章,努力保证质量,希望小伙伴们能多多提意见,咱们共同学习,共同进步。
上一篇传送门:Build your own React_7 函数组件
以上是关于Build your own React_8 Hooks的主要内容,如果未能解决你的问题,请参考以下文章
Build your own React_4 理解React纤维