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_8 Hooks

Build your own React_4 理解React纤维

Build your own React_4 理解React纤维

Build your own React_0 总述

Build your own React_0 总述

Build your own React_7 函数组件