咋说呢这里的实现只能是大部分, 细节其实很粗糙, 有的地方还有bug, 但是不影响对fiber的理解. 尤其是data structure的应用
folder structure
src
├── core
│ ├── h.js // jsx function, create virtual node
│ ├── life-cycle.js // unmount/mount
│ └── with-state.js // like useState in react
├── dom // this is like react-dom, manipulate dom, expose `render` function
│ ├── config.js
│ ├── constants.js
│ ├── index.js
│ └── utils // dom operaions
│ ├── append.js
│ ├── createElement.js
│ ├── getDocumentByElement.js
│ ├── insert.js
│ ├── remove.js
│ ├── textElement.js
│ └── validate.js
├── fiber // filber implementation
│ ├── begin-work.js // start to process fiber node
│ ├── children.js // ! reconciler children
│ ├── commit-work.js // commit phase
│ ├── complete-work.js // will call this after each fiber got processed
│ ├── f-life-cycle.js
│ ├── f-node.js // fiber node
│ ├── f-with.js // state node, useState will created a state node in fiber
│ ├── host-context.js // statck to preserve a global host variable
│ ├── reconciler.js // ! this is the most important one, pls see react official doc about reconciliation
│ ├── root-render.js
│ ├── scheduler.js // requestIdleCallback
│ └── stack.js
├── shared
│ ├── effect-tag.js // update/insertion/deletion tag
│ ├── shallowEqual.js
│ ├── status-work.js
│ ├── tag.js // tag of each kind of fiber node
│ ├── types.js
│ ├── validate.js
│ └── with-effect.js //
└── structures
└── linked-list.js
common types
// type: List<Tuple<Key, Value>>
updatePayload
// state node, created by calling useState
// the basic structure is this
//
// a [fiber node]
// |
// [ state node #1] <- [a, dispatchA] = useState(1)
// | |
// | [queue node #1] <- dispatchA(2)
// | |
// | [queue node #2] <- dispatchA(3)
// [ state node #1] <- [b, dispatchB] useState(1)
// |
// [queue node #1] <- dispatchB(a)
// |
// [queue node #2] <- dispatchB(b)
common code
// double assignment
a = b = c
// is
b = c;
a = b;
//
WIP // refer to current working fiber node
current: FNode // refer to active fiber node
vnode // react virtual node
fnode // react fiber node
// in children.js
function placeChild(newFNode, lastPlacedIndex, newIndex) {
newFNode.index = newIndex;
const current = newFNode.alternate;
if (current !== null) {
const oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
// this is a move
newFNode.effectTag = Placement;
return lastPlacedIndex;
} else {
// this item can stay in place
return oldIndex;
}
} else {
// this is an insertion.
newFNode.effectTag = Placement;
return lastPlacedIndex;
}
}
classic DFS search in Fiber
function appendAllChildren(
parent,
WIP
) {
let node = WIP.child;
// traverse all nodes
while (node !== null) {
if (node.tag === DNode || node.tag === Text) {
// update dom
appendInitialChild(parent, node.instanceNode);
// ^ it should back from here
// node = node.return
} else if (node.child !== null) {
// ^ why else if
// dive down
node.child.return = node;
node = node.child;
continue;
}
// back to beginning
if (node === WIP) {
return;
}
// try sibling
while (node.sibling === null) {
if (node.return === null || node.return === WIP) {
return;
}
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
react-fiber is my self-study project help me understand how react work. In fact, all �codebase re-implement each step , so it looks similar to the source code of react. Though, I think it's still smaller and easier to understand than when you actually read the react source code. I hope it helpful for people who want to start learn how react fiber work.
- Single linked list, Circular linked list
- Simple stack and queue
- Recursive
- Structural sharing
- Reconciliation
- Scheduler
- Bitwise Operators
- JSX
- DOM
- React Components, Elements, and Instances
- Design Principles
- React Fiber resources
- The how and why on React’s usage of linked list in Fiber to walk the component’s tree
- In-depth explanation of state and props update in React
- Lin Clark - A Cartoon Intro to Fiber - React Conf 2017
- A look inside React Fiber
- Build your own React Fiber
- React Fiber Architecture @acdlite and React Fiber Architecture @SaeedMalikx
Inside Fiber: in-depth overview of the new reconciliation algorithm in React
work (unitOfWork): A component, node element => fiber
current: Current fiber what is displayed on browser
WIP (workInProgress): New fiber tree we will build
fiber: {
type: string | Function ('div', 'span', function Button)
instanceNode: HTMLElement (div, span)
return: fiber (parent of fiber)
child: fiber (child of fiber)
sibling: fiber (sibling of fiber)
alternate: link current - WIP and WIP - current
effectTag: number (give we know what will happen this fiber)
}
requestIdleCallback
main function:
createWorkInProgress()
beginWork()
reconcileChildren()
completeWork()
commitWork()
Render -> Reconciler -> Scheduler ->
Begin Work (build fiber tree) -> ChildReconciler(create child and effectTag) -> if work has child we will continue to run beginWork -> no child ->
Complete Work (build list effect, mark tag and create instanceNode) -> sibling has child -> turn back Begin Work -> no child -> Complete Work -> no sibling -> has a new tree with effect tag ->
Commit Work : It will base on list effect tag to commit each fiber (Placement, Update, Delete, Lifecycle)
// In first render current fiber is null.
// current is workInProgress when commit
Do something ->
Get current Fiber what corresponding to the component ->
Recursive to find Root ->
Clone fiber from root to component has update ->
Begin Work from this fiber (it's maybe clone fiber when children of component use memo, pure component or use shouldComponentUpdate) ->
Complete Work ->
Commit Work
Hooks are stored as a linked list on the fiber's prevState field of fiber.
current tree - current hook <=> WIP - WIP hook