1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

簡易 React 自作 で 分かりづらかったところ

Last updated at Posted at 2022-08-22

簡易 React 自作 を試してみて

React 自作の記事があったので、試してみました。

ただ、やってみて分かりづらい部分が多々あったので、そのまとめをします。

reconcileChildren の中の prevSibling.sibling = newFiber ってなんだ?

if (index === 0) {
	wipFiber.child = newFiber
} else {
    prevSibling.sibling = newFiber
}
prevSibling = newFiber

これは github に答えがありました。

1:まず、初回のループ(index === 0)の時は、wipFiber.child に 初回の newFiber を追加して、prevSibling に 初回の newFiber を代入します。
2:次のループ(index === 1)の時は、prevSibling に入っている 初回の newFiber の siblingプロパティーに 2回目の newFiber が代入されます。そして、prevSibling に 2回目の newFiber が代入されます。
3:その次以降のループも同じようになる。

言ってしまえば、簡単ですね。
これで、各 parent と 最初の各children のみが連結され、sibling は 最初の child から数珠繋ぎになっている、二重連鎖木 ができますね。

alternate って、最初の render の時にしか定義されていないような・・・?

alternate と言えば、reconcileChildren で古いFiber と新しいFiberを比較するためのプロパティですね。

このalternate、コードを読むと代入されている部分が3箇所のみで、使われているのが2箇所のみになっています(「Step6:差分検出」まででは)。

代入されている部分

初回render時
function render(element, container) {
	wipRoot = {
		dom: container,
		props: {
			children: [element]
		},
		alternate: currentRoot
	}
	deletions = []
	nextUnitOfWork = wipRoot
}
ノードの更新時
if (sameType) {
	// TODO Update node
	newFiber = {
		type: oldFiber.type,
		props: element.props,
		dom: oldFiber.dom,
		parent: wipFiber,
		alternate: oldFiber,
		effectTag: "UPDATE"
	}
}
ノードの作成時
if (element && !sameType) {
	// TODO Add node
	newFiber = {
		type: element.type,
		props: element.props,
		dom: null,
		parent: wipFiber,
		alternate: null,
		effectTag: "PLACEMENT"
	}
}

使われている部分

旧Fiber の 取得
let oldFiber = wipFiber.alternate && wipFiber.alternate.child
旧Fiber の Props を取得
updateDom(fiber.dom, fiber.alternate.props, fiber.props)

ここで、一度 "PLACEMENT" が終わった後で、再レンダリングし "UPDATE" が行われた場合を考える。

onInput で 再レンダリング
// UseCase2
const onInputWord = (event) => {
	rerender(event.target.value)
}
const container = document.getElementById("root")
const rerender = value => {
	const element = Didact.createElement(
		"div",
		{ id: "foo"},
		Didact.createElement("a", null, Didact.createElement("h2", null, "bar")),
		Didact.createElement("br"),
		Didact.createElement("b", null,
			Didact.createElement("h4", null,
				Didact.createElement("i", null, `Hello ${value}`)
			)
		),
		Didact.createElement("br"),
		Didact.createElement("input", {onInput: onInputWord, defaultValue: value})
	)
	Didact.render(element, container)
}
rerender("World")

この使われている部分の wipFiber.alternate && wipFiber.alternate.child が、初回 render時 の alternate が代入されている時に入るのは分かるが、次の wipFiber.child が nextUnitOfWork に入る

if (fiber.child) {
	return fiber.child
}

では、どうなっているか分かりづらい。

ただ、ここの順番は、

const elements = fiber.props.children
reconcileChildren(fiber, elements)

if (fiber.child) {
	return fiber.child
}

レコンサイル (reconcileChildren) して、fiber に child オプションなどを付与してから 次の performUnitOfWork に移っているので、次の performUnitOfWork の中の reconcileChildren では alternate が null になることはない。

初回の render 時のレコンサイルから説明すると、render して alternate が付与された nextUnitOfWork = wipRoot は、まず nextUnitOfWork が null でなくなったので、performUnitOfWork で reconcileChildren します。
reconcileChildren では、wipRoot が wipFiber に入っているので、oldFiber には null は入りません。
ここで、sameType の判定が入りますが、ここでは oldFiber に入っている alternate.child と element[0] は同じなので(wipRootの子要素のtypeが変わっていない限り。なお今回のインプットフォームではwipRootの子要素のtypeは変わりません)、alternate に oldFiber が入った newFiber が作成され、wipFiber の child に newFiber が代入されます。
二回目の index の while では、wipRootの子要素のtypeが変わっていないので、上とほぼ同じですが、prevSibling の sibling に newFiber が代入されます。

この reconcileChildren が終了すると、child が見つかる場合は performUnitOfWork (fiber.child) が実行されますが、ここの fiber.child にも 二度目の rerender 時には alternate が付与されています。
Sibling の場合にも、二度目の rerender 時には alternate が付与されます。
reconcileChildren の後で if (fiber.child) している順番通りです。

まとめると、

const elements = fiber.props.children
reconcileChildren(fiber, elements)

if (fiber.child) {
	return fiber.child
}
// 省略
			return nextFiber.sibling

の順番通り、reconcileChildren で child と sibling に alternate が入った newFiber を代入してから、return される child と 各sibling で performUnitOfWork (nextUnitOfWork) をします。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?