Slide 1

Slide 1 text

"MHPSJUINTJO3FBDU !LPCB CVJMEFSTDPO4FQ

Slide 2

Slide 2 text

LPCB

Slide 3

Slide 3 text

We Are Hiring!!

Slide 4

Slide 4 text

8IBU*UBMLBCPVU • Why does React replace its implementation from Stack to Fiber? • The concept of Fiber • How to solve the problems • Algorithms that Fiber uses

Slide 5

Slide 5 text

8IBUJT3FBDU

Slide 6

Slide 6 text

https://reactjs.org

Slide 7

Slide 7 text

const LikeApp = props => Like App!

{props.likeCount}


 like ; let likeCount = 0; const update = () => ++likeCount; const render = () => ReactDOM.render( update() && render()} />, document.getElementById(‘app’) ); render(); ←only update the UI ←render the whole App

Slide 8

Slide 8 text

-FUnTEJWFJOUP3FBDU JOUFSOBMT

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

"SDIJUFDUVSFPG3FBDU

Slide 11

Slide 11 text

"SDIJUFDUVSFPG3FBDU
Stack Fiber DOM(Stack) Native(Stack) Canvas etc Component Reconciler Renderer HTMLElement SVGElement UIView Host CanvasElement Native(Fiber) DOM(Fiber) Fiber(Wasm?)

Slide 12

Slide 12 text

4UBDL3FDPODJMFS

Slide 13

Slide 13 text

4UBDL3FDPODJMFS • ʙv15 • Traverse and process a ReactElement tree recursively • Diff and Patch are the same time during traversing ReactElement tree • Process synchronously • Create a public instance and an internal instance

Slide 14

Slide 14 text

3FBDU&MFNFOU • ReactElement is an object represents the corresponding DOM • Recreate each calling the render function • Lightweight • No state

Slide 15

Slide 15 text

1VCMJD*OTUBODF • An instance of Composite Component that you create with React.Component • The lifecycle is mounting ʙ unmounting

Slide 16

Slide 16 text

*OUFSOBM*OTUBODF • An instance that React uses internally • The lifecycle is mounting ʙ unmounting • Stack traverses a React Component tree through the internal instance • ReactInstanceMap.set(publicInstance, internalInstance);

Slide 17

Slide 17 text

3FBDU*OTUBODF.BQ var ReactInstanceMap = { remove: function(key) { key._reactInternalInstance = undefined; }, get: function(key) { return key._reactInternalInstance; }, has: function(key) { return key._reactInternalInstance !== undefined; }, set: function(key, value) { key._reactInternalInstance = value; }, }; /15-stable/src/renderers/shared/shared/ReactInstanceMap.js

Slide 18

Slide 18 text

ReactElement InternalInstance ReactElement ReactElement PublicInstance Mount Unmount

Slide 19

Slide 19 text

Reconciler.mountComponent InternalInstance.mountComponent Reconciler.mountComponent InternalInstance.mountComponent Reconciler.mountComponent InternalInstance.mountComponent … child child child recursive

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

8IBUJTUIFQSPCMFN

Slide 22

Slide 22 text

5IFQSPCMFNTPG4UBDL • Stack must process a ReactElement tree synchronously because it traverse the tree with recursive calls • It leads blocking the UI thread with a big ReactElement tree • It takes time to traverse the tree even though you have only small updates • In order to avoid that, you have to implement shouldComponentUpdate in your components

Slide 23

Slide 23 text

Sync

Slide 24

Slide 24 text

'JCFS3FDPODJMFS

Slide 25

Slide 25 text

'JCFS3FDPODJMFS • Traverse and process a ReactElement tree through Fiber objects • Render Phase and Commit Phase • Fiber can suspend and resume in its Render Phase

Slide 26

Slide 26 text

'JCFS • Fiber is an unit of work • A ReactElement tree becomes A Linked list of Fibers • Fiber is a fixed data structure, which can share the same hidden class • A Fiber has an alternate Fiber • Double Buffer (workInProgress <-> current)

Slide 27

Slide 27 text

https://en.wikipedia.org/wiki/Fiber_(computer_science)

Slide 28

Slide 28 text

type Fiber = {| tag: WorkTag, type: any, stateNode: any, return: Fiber | null, child: Fiber | null, sibling: Fiber | null, pendingProps: any, memoizedProps: any, memoizedState: any, updateQueue: UpdateQueue | null, mode: TypeOfMode, effectTag: TypeOfSideEffect, nextEffect: Fiber | null, expirationTime: ExpirationTime, alternate: Fiber | null, : |} /packages/react-reconciler/src/ReactFiber.js

Slide 29

Slide 29 text

-JOLFE-JTU • A ReactElement tree becomes A Linked list of Fibers • Fiber processes sequentially, doesn’t need random access • Search … O(n) • Insert/Delete … O(1)

Slide 30

Slide 30 text

https://en.wikipedia.org/wiki/Linked_list

Slide 31

Slide 31 text

HostRoot App child parent UserList child parent User child parent User sibling koba01 koba02 child parent parent child parent

Slide 32

Slide 32 text

child parent child parent Insert child parent Delete child child parent parent

Slide 33

Slide 33 text

let root = fiber; let node = fiber; while (true) { if (node.child && !hasProcessed(node.child)) { node = node.child; continue; } if (node === root) { return; } while (!node.sibling) { if (!node.parent) { return; } node = node.parent;
 } node = node.sibling; } Search the children Reach the root Return to the root Reach the root Search the siblings

Slide 34

Slide 34 text

HostRoot App UserList User User koba01 koba02 child child child parent sibling child parent parent parent child parent

Slide 35

Slide 35 text

)PXUPBDIJFWF BTZODISPOPVTSFOEFSJOH

Slide 36

Slide 36 text

%PVCMF#VGGFS HostRoot App child parent section child parent … alternate HostRoot App child parent section child parent … Current Work in Progress alternate alternate alternate

Slide 37

Slide 37 text

%PVCMF#VGGFS HostRoot App child parent section child parent … alternate HostRoot App child parent section child parent … (Work In Progress) Current alternate alternate alternate Commit!

Slide 38

Slide 38 text

https://www.gameprogrammingpatterns.com/double-buffer.html

Slide 39

Slide 39 text

3FOEFS1IBTF BOE $PNNJU1IBTF

Slide 40

Slide 40 text

FiberA FiberB FiberC FiberD FiberE IdleTime IdleTime IdleTime Render Phase (Async) Commit Phase (Sync) SideEffect SideEffect SideEffect Commit SideEffects

Slide 41

Slide 41 text

3FOEFS1IBTF $PNNJU1IBTF • Prepare for updating • Render Phase can be async • Extract Side Effects • No Side Effects • Interruptible • Apply Side Effects that affect to the Host • Commit Phase must be sync • Call some lifecycle methods • Consistency

Slide 42

Slide 42 text

let nextUnitOfWork = findNextUnitOfWork(); const workLoop = () => { while (nextUnitOfWork !== null) { const workInProgress = nextUnitOfWork; nextUnitOfWork = beginWork(workInProgress); if (nextUnitOfWork === null) { nextUnitOfWork = completeWork(workInProgress); ɹɹ } if (!hasRemainingTime()) {
 scheduleUpdate(workLoop); return; } } commitWork(); }

Slide 43

Slide 43 text

HostRoot App child parent UserList child parent User child parent User sibling koba01 koba02 child parent parent child parent beginWork completeWork

Slide 44

Slide 44 text

Async

Slide 45

Slide 45 text

8IBUJTUIF4JEF&GGFDU

Slide 46

Slide 46 text

4JEF&GGFDU • Side Effect is set to Fiber at Render Phase • Side Effect is processed at Commit Phase • Because it must process at the right time • Fiber has nextEffect property as the pointer to apply Side Effects by the right order

Slide 47

Slide 47 text

export const NoEffect = 0b00000000000; export const PerformedWork = 0b00000000001; export const Placement = 0b00000000010; export const Update = 0b00000000100; export const PlacementAndUpdate = 0b00000000110; export const Deletion = 0b00000001000; export const ContentReset = 0b00000010000; export const Callback = 0b00000100000; export const DidCapture = 0b00001000000; export const Ref = 0b00010000000; export const Snapshot = 0b00100000000; export const Incomplete = 0b01000000000; export const ShouldCapture = 0b10000000000; /packages/shared/ReactSideEffectTags.js

Slide 48

Slide 48 text

HostRoot App UserList User User koba01 koba04 nextEffect nextEffect nextEffect firstEffect PureComponent

Slide 49

Slide 49 text

)PXUPNBOBHF NVMUJQMFVQEBUFT

Slide 50

Slide 50 text

6QEBUF2VFVF • Update Queue is a Linked list • Update Queue is stored in a Fiber that invoke the update • Double Buffering to interrupt the other updates • workInProgress 㲗 current • You can imagine Git rebase as a metaphor

Slide 51

Slide 51 text

type Update = { expirationTime: ExpirationTime, tag: 0 | 1 | 2 | 3, payload: any, callback: (() => mixed) | null, next: Update | null, nextEffect: Update | null, }; type UpdateQueue = { baseState: State, firstUpdate: Update | null, lastUpdate: Update | null, firstEffect: Update | null, lastEffect: Update | null, : } /packages/react-reconciler/src/ReactUpdateQueue.js

Slide 52

Slide 52 text

if (queue.lastUpdate === null) {
 queue.firstUpdate = queue.lastUpdate = update; } else { queue.lastUpdate.next = update; queue.lastUpdate = update; } Append a update to the Update Queue /packages/react-reconciler/src/ReactUpdateQueue.js

Slide 53

Slide 53 text

class App extends React.Component { constructor(props) { this.state = { count: 1 } } render() { return (

{this.state.count}

this.setState(state => ({ count: state.count + 1}))} > ++ ); } }

Slide 54

Slide 54 text

HostRoot App child parent section child parent … alternate HostRoot App child parent section child parent … alternate alternate alternate Update Queue Update Queue

Slide 55

Slide 55 text

A Current Queue WorkInProgress Queue B C D C D Commit A Current Queue WorkInProgress Queue B C D C D

Slide 56

Slide 56 text

A Current Queue WorkInProgress Queue B C D C D Abort Current Queue WorkInProgress Queue A B C D A B C D

Slide 57

Slide 57 text

A(High) Update Queue 1st Update B(Low) C(High) D(Low) A(High) C(High) 2nd Update B(Low) C(High) D(Low) A(High) Rebase baseState

Slide 58

Slide 58 text

this.setState({ count: this.state.count + 1 }); this.setState(state => ({ count: state.count + 1 }));

Slide 59

Slide 59 text

)PXUPQSJPSJUJ[F UIFVQEBUFT

Slide 60

Slide 60 text

&YQJSBUJPO5JNF • React had used priority levels to prioritize the updates • The approach of priority levels has a Starvation Problem • In order to solve this, React uses Expiration Time • If an expiration time has come, the update processes synchronously

Slide 61

Slide 61 text

https://en.wikipedia.org/wiki/Starvation_(computer_science)

Slide 62

Slide 62 text

export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5; module.exports = { NoWork: 0, SynchronousPriority: 1, AnimationPriority: 2, HighPriority: 3, LowPriority: 4, OffscreenPriority: 5, }; /15-stable/src/renderers/shared/fiber/ReactPriorityLevel.js

Slide 63

Slide 63 text

export const NoWork = 0; export const Sync = 1; export const Never = MAX_SIGNED_31_BIT_INT; const UNIT_SIZE = 10; const MAGIC_NUMBER_OFFSET = 2; function ceiling(num: number, precision: number): number { return (((num / precision) | 0) + 1) * precision; } function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs): ExpirationTime { return ( MAGIC_NUMBER_OFFSET + ceiling( currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE, ) ); } export const LOW_PRIORITY_EXPIRATION = 5000; export const LOW_PRIORITY_BATCH_SIZE = 250; export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150; export const HIGH_PRIORITY_BATCH_SIZE = 100 /packages/react-reconciler/src/ReactFiberExpirationTime.js

Slide 64

Slide 64 text

100 90 120 50 100 90 120 100 120 1 Sync 1st 2nd 3rd

Slide 65

Slide 65 text

*OUFSBDUJWF&WFOU5ZQF • React treats some event types as interactive events • Updates in interactive events are prioritized as High Priorities • Interactive Events … • blur, click, dragStart, drop, focus, input, keyDown, mouseDown, play, pause, submit, touchStart… • /packages/react-dom/src/events/SimpleEventPlugin.js

Slide 66

Slide 66 text

{ this.setState({ inputText: value }); // High Priority requestAnimationFrame(() => { this.setState({ filterValue: value }); // Low Priority }); }} />

Slide 67

Slide 67 text

%FNP https://github.com/koba04/react-timeslicing-demo

Slide 68

Slide 68 text

#JUXJTF0QFSBUPST

Slide 69

Slide 69 text

#JUXJTF0QFSBUPST • Fiber uses Bitwise operations in many places internally • Mode • Effect • Context API - ObservedBits • (Simple Cache Provider - CalculateChangedBits)

Slide 70

Slide 70 text

export const PerformedWork = 0b00000000001; export const Placement = 0b00000000010; export const Update = 0b00000000100; export const PlacementAndUpdate = 0b00000000110; export const Deletion = 0b00000001000; export const ContentReset = 0b00000010000; export const Callback = 0b00000100000; export const DidCapture = 0b00001000000; export const Ref = 0b00010000000; export const Snapshot = 0b00100000000; export const LifecycleEffectMask = 0b00110100100; export const HostEffectMask = 0b00111111111; /packages/shared/ReactSideEffectTags.js

Slide 71

Slide 71 text

// Render Phase (/packages/react-reconciler/src/ReactFiberCompleteWork.js) next.effectTag &= HostEffectMask; sourceFiber.effectTag &= ~LifecycleEffectMask; function markUpdate(workInProgress: Fiber) { workInPregress.effectTag |= Update; } // CommitPhase (/packages/react-reconciler/src/ReactFiberCommitWork.js) if (finishedWork.effectTag & Update) { if (current === null) { : instance.componentDidMount(); } else { : } }

Slide 72

Slide 72 text

// Mode export const NoContext = 0b000; export const AsyncMode = 0b001; export const StrictMode = 0b010; export const ProfileMode = 0b100; if (workInProgress.mode & StrictMode) { // do something } const pingTime = (workInProgress.mode & AsyncMode) === NoEffect ? Sync : renderExpirationTime; /packages/react-reconciler/src/ReactTypeOfMode.js

Slide 73

Slide 73 text

const observedBits = { foo: 0b01, bar: 0b10 }; const { Consumer } = React.createContext(store, (prev, next) => { let result = 0; if (prev.foo !== next.foo) result |= observedBits.foo; if (prev.bar !== next.bar) result |= observedBits.bar; return result; }); const Foo = () => {({foo}) => …} const Bar = () => {({bar}) => …}

Slide 74

Slide 74 text

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Examples

Slide 75

Slide 75 text

4VTQFOTF

Slide 76

Slide 76 text

)PXUPNBOBHFBTZOD EFQFOEFODJFT

Slide 77

Slide 77 text

&SSPS#PVOEBSJFT HostRoot App UserList User Text Throw Error!!! ErrorPage Error Error componentDidCatch

Slide 78

Slide 78 text

4VTQFOTF HostRoot App UserList User Text Throw Promise!!! Promise React. Placeholder If Promise has not yet been resolved -> Suspend has been resolved -> Resume the rendering had not resolved -> Display Fallback Component Fallback

Slide 79

Slide 79 text

const App = () => ( }> ); const EntrySection = () => ; const Entry = () => { // throw Promise if a cache doesn’t have an entry data const entry = fetchEntryWithCache(); return

{entry.data}

; };

Slide 80

Slide 80 text

How to handle tryʙcatch in async reconciliation?

Slide 81

Slide 81 text

4VTQFOTF HostRoot App UserList User Text Throw Promise!!! React. Placeholder Fallback parent parent Fiber can trace the component stack through the Linked List structures

Slide 82

Slide 82 text

%FNP https://github.com/koba04/react-suspense-demo

Slide 83

Slide 83 text

4VTQFOTF • Suspense can suspend a rendering by throwing a Promise • tryʙcatch can’t catch thrown objects in Async Rendering • Fiber can trace the component stack through Linked List • Still in development

Slide 84

Slide 84 text

3FDBQ • React has replaced its implementation from Stack to Fiber to solve the problems. • Fiber makes async and flexible scheduling possible • Fiber uses many technics internally like LinkedList, Double Buffer, Expiration Time, Bitwise Operators etc.

Slide 85

Slide 85 text

5IBOLZPV TQFBLFSEFDLDPNLPCB