Rewrote the React Fiber algorithm.
okmttdhr / my-own-react
Rewrite React Fiber core algorithm in 300 lines with links to the React source code
It's about 300 lines long, but I've tried to re-implement the real React as closely as possible, including the naming and function's scope. The implementation was focused on the following points;
- The data structure and algorithm of Fiber
- An interruptible unit of work (conceptually same as concurrent mode)
- Render and commit phase
Architecture;
I have also commented a link to the React source code, so you can compare it for a better understanding. (For example, in this case, requestIdleCallback
is used to implement pseudo-scheduling, but React is using its own Scheduler).
Since there is already a lot of information about the architecture of Fiber, this article will only introduce some pieces of code.
performUnitOfWork
Traverse the Fiber to determine the next unit of work.
// https://github.com/okmttdhr/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L3206
function beginWork(fiber) {
const isFunctionComponent =
fiber.type instanceof Function
if (isFunctionComponent) {
updateFunctionComponent(fiber)
} else {
updateHostComponent(fiber)
}
if (fiber.child) {
return fiber.child
}
let nextFiber = fiber
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling
}
nextFiber = nextFiber.parent
}
}
// https://github.com/facebook/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberWorkLoop.new.js#L1574
function performUnitOfWork(fiber) {
nextUnitOfWork = beginWork(fiber)
if (!nextUnitOfWork) {
commitRoot()
completeUnitOfWork()
}
}
workLoop
Loop until it runs out of work.
When the browser gets busy, stop the loop and come back when it's done.
// https://github.com/facebook/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L1567
function workLoop(deadline) {
let shouldYield = false
while (workInProgressRoot && !shouldYield) {
performUnitOfWork(nextUnitOfWork)
shouldYield = deadline.timeRemaining() < 1
}
requestIdleCallback(workLoop)
}
reconcileChildren
Diffing and updating Fiber
// https://github.com/facebook/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L255
function reconcileChildren(workInProgressFiber, elements) {
let index = 0
let oldFiber =
workInProgressFiber.alternate && workInProgressFiber.alternate.child
let prevSibling = null
while (
index < elements.length ||
oldFiber != null
) {
const element = elements[index]
let newFiber = null
const sameType =
oldFiber &&
element &&
element.type == oldFiber.type
if (sameType) {
newFiber = {
type: oldFiber.type,
props: element.props,
dom: oldFiber.dom,
parent: workInProgressFiber,
alternate: oldFiber,
flag: UPDATE,
}
}
if (element && !sameType) {
newFiber = {
type: element.type,
props: element.props,
dom: null,
parent: workInProgressFiber,
alternate: null,
flag: PLACEMENT,
}
}
if (oldFiber && !sameType) {
oldFiber.flag = DELETION
deletions.push(oldFiber)
}
if (oldFiber) {
oldFiber = oldFiber.sibling
}
if (index === 0) {
workInProgressFiber.child = newFiber
} else if (element) {
prevSibling.sibling = newFiber
}
prevSibling = newFiber
index++
}
}
commitWork
Update DOM (Commit phase)
// https://github.com/facebook/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberCommitWork.new.js#L1814
function commitWork(fiber) {
if (!fiber) {
return
}
let parentFiber = fiber.parent
while (!parentFiber.dom) {
parentFiber = parentFiber.parent
}
const parentDom = parentFiber.dom
if (
fiber.flag === PLACEMENT &&
fiber.dom != null
) {
commitPlacement(fiber, parentDom)
} else if (
fiber.flag === UPDATE &&
fiber.dom != null
) {
commitUpdate(
fiber.dom,
fiber.alternate.props,
fiber.props
)
} else if (fiber.flag === DELETION) {
commitDeletion(fiber, parentDom)
}
commitWork(fiber.child)
commitWork(fiber.sibling)
}
// https://github.com/facebook/react/blob/84c06fef8168e779d15cc9450f67888445f7b4f4/packages/react-reconciler/src/ReactFiberWorkLoop.new.js#L1693
function commitRoot() {
deletions.forEach(commitWork)
commitWork(workInProgressRoot.child)
}
Comments and PRs are welcome!
Top comments (0)