Skip to content

Commit 4e72c81

Browse files
committed
Using expirationTime and ChildExpiration in fiber
Related pr facebook/react#13139
1 parent c6a3e58 commit 4e72c81

File tree

6 files changed

+236
-125
lines changed

6 files changed

+236
-125
lines changed

src/ChildFiber.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
createFiberFromElement,
44
createFiberFromText,
55
} from "./Fiber";
6-
import { NoEffect, Deletion, Placement } from "./TypeOfSideEffect";
6+
import { Deletion, Placement } from "./TypeOfSideEffect";
77
import { HostText } from "./TypeOfWork";
88
import { REACT_ELEMENT_TYPE } from "./createElement";
99

src/Fiber.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from "./TypeOfWork";
1010
import { REACT_ASYNC_MODE_TYPE, REACT_STRICT_MODE_TYPE } from "./createElement";
1111
import { AsyncMode, StrictMode } from "./TypeOfMode";
12+
import { NoWork } from "./FiberExpirationTime";
1213

1314
export class FiberNode {
1415
constructor(tag, pendingProps, key, mode) {
@@ -31,18 +32,28 @@ export class FiberNode {
3132
this.memoizedState = null;
3233

3334
this.mode = mode;
35+
3436
// Effects
3537
this.effectTag = NoEffect;
3638
this.nextEffect = null;
3739
this.firstEffect = null;
3840
this.lastEffect = null;
41+
42+
this.expirationTime = NoWork;
43+
this.childExpirationTime = NoWork;
44+
3945
this.alternate = null;
4046
}
4147
}
4248

4349
export function createWorkInProgress(current, pendingProps, expirationTime) {
4450
let workInProgress = current.alternate;
45-
if (workInProgress == null) {
51+
if (workInProgress === null) {
52+
// We use a double buffering pooling technique because we know that we'll
53+
// only ever need at most two versions of a tree. We pool the "other" unused
54+
// node that we're free to reuse. This is lazily created to avoid allocating
55+
// extra objects for things that are never updated. It also allow us to
56+
// reclaim the extra memory if needed.
4657
workInProgress = new FiberNode(
4758
current.tag,
4859
pendingProps,
@@ -55,21 +66,34 @@ export function createWorkInProgress(current, pendingProps, expirationTime) {
5566
workInProgress.alternate = current;
5667
current.alternate = workInProgress;
5768
} else {
69+
workInProgress.pendingProps = pendingProps;
70+
71+
// We already have an alternate.
72+
// Reset the effect tag.
5873
workInProgress.effectTag = NoEffect;
5974

75+
// The effect list is no longer valid.
6076
workInProgress.nextEffect = null;
6177
workInProgress.firstEffect = null;
6278
workInProgress.lastEffect = null;
6379
}
6480

65-
workInProgress.expirationTime = expirationTime;
66-
workInProgress.pendingProps = pendingProps;
81+
// Don't touching the subtree's expiration time, which has not changed.
82+
workInProgress.childExpirationTime = current.childExpirationTime;
83+
if (pendingProps !== current.pendingProps) {
84+
// This fiber has new props.
85+
workInProgress.expirationTime = expirationTime;
86+
} else {
87+
// This fiber's props have not changed.
88+
workInProgress.expirationTime = current.expirationTime;
89+
}
6790

6891
workInProgress.child = current.child;
6992
workInProgress.memoizedProps = current.memoizedProps;
7093
workInProgress.memoizedState = current.memoizedState;
7194
workInProgress.updateQueue = current.updateQueue;
7295

96+
// These will be overridden during the parent's reconciliation
7397
workInProgress.sibling = current.sibling;
7498
workInProgress.index = current.index;
7599
workInProgress.ref = current.ref;

src/FiberBeginWork.js

Lines changed: 110 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
Mode,
1616
} from "./TypeOfWork";
1717
import { prepareToUseHooks, finishHooks, bailoutHooks } from "./FiberHooks";
18+
import { NoWork } from "./FiberExpirationTime";
1819

1920
let didReceiveUpdate = false;
2021

@@ -26,15 +27,23 @@ export function beginWork(current, workInProgress, renderExpirationTime) {
2627
didReceiveUpdate = true;
2728
} else if (updateExpirationTime < renderExpirationTime) {
2829
didReceiveUpdate = false;
30+
return bailoutOnAlreadyFinishedWork(
31+
current,
32+
workInProgress,
33+
renderExpirationTime,
34+
);
2935
}
3036
} else {
3137
didReceiveUpdate = false;
3238
}
3339

40+
// Before entering the begin phase, clear the expiration time.
41+
workInProgress.expirationTime = NoWork;
42+
3443
switch (workInProgress.tag) {
3544
case FunctionalComponent:
3645
const Component = workInProgress.type;
37-
return updateFunctionalComponent(
46+
return updateFunctionComponent(
3847
current,
3948
workInProgress,
4049
Component,
@@ -49,33 +58,47 @@ export function beginWork(current, workInProgress, renderExpirationTime) {
4958
case HostRoot:
5059
return updateHostRoot(current, workInProgress, renderExpirationTime);
5160
case HostComponent:
52-
return updateHostComponent(current, workInProgress);
61+
return updateHostComponent(current, workInProgress, renderExpirationTime);
5362
case HostText:
5463
return updateHostText(current, workInProgress);
5564
case Mode:
56-
return updateMode(current, workInProgress);
65+
return updateMode(current, workInProgress, renderExpirationTime);
5766
}
5867
}
5968

60-
export function updateHostRoot(current, workInProgress, renderExpirationTime) {
61-
let updateQueue = workInProgress.updateQueue;
62-
if (updateQueue != null) {
63-
let state = processUpdateQueue(
69+
function updateHostRoot(current, workInProgress, renderExpirationTime) {
70+
const updateQueue = workInProgress.updateQueue;
71+
const nextProps = workInProgress.pendingProps;
72+
const prevState = workInProgress.memoizedState;
73+
const prevChildren = prevState !== null ? prevState.element : null;
74+
processUpdateQueue(
75+
current,
76+
workInProgress,
77+
updateQueue,
78+
nextProps,
79+
null,
80+
renderExpirationTime,
81+
);
82+
const nextState = workInProgress.memoizedState;
83+
const nextChildren = nextState.element;
84+
85+
if (nextChildren === prevChildren) {
86+
return bailoutOnAlreadyFinishedWork(
6487
current,
6588
workInProgress,
66-
updateQueue,
67-
null,
68-
null,
6989
renderExpirationTime,
7090
);
71-
reconcileChildren(current, workInProgress, state.element);
72-
workInProgress.memoizedState = state;
73-
return workInProgress.child;
7491
}
75-
return bailoutOnAlreadyFinishedWork(current, workInProgress);
92+
reconcileChildren(
93+
current,
94+
workInProgress,
95+
nextChildren,
96+
renderExpirationTime,
97+
);
98+
return workInProgress.child;
7699
}
77100

78-
export function updateFunctionalComponent(
101+
function updateFunctionComponent(
79102
current,
80103
workInProgress,
81104
Component,
@@ -98,12 +121,16 @@ export function updateFunctionalComponent(
98121
}
99122

100123
workInProgress.effectTag |= PerformedWork;
101-
reconcileChildren(current, workInProgress, nextChildren);
102-
workInProgress.memoizedProps = nextProps;
124+
reconcileChildren(
125+
current,
126+
workInProgress,
127+
nextChildren,
128+
renderExpirationTime,
129+
);
103130
return workInProgress.child;
104131
}
105132

106-
export function updateHostText(current, workInProgress) {
133+
function updateHostText(current, workInProgress) {
107134
if (current == null) {
108135
workInProgress.effectTag |= Placement;
109136
}
@@ -112,28 +139,23 @@ export function updateHostText(current, workInProgress) {
112139
return null;
113140
}
114141

115-
export function updateHostComponent(current, workInProgress) {
142+
function updateHostComponent(current, workInProgress, renderExpirationTime) {
116143
if (current == null) {
117144
workInProgress.effectTag |= Placement;
118145
}
119146
const memoizedProps = workInProgress.memoizedProps;
120147
let nextProps = workInProgress.pendingProps || memoizedProps;
121-
122-
if (nextProps == null || memoizedProps === nextProps) {
123-
return bailoutOnAlreadyFinishedWork(current, workInProgress);
124-
}
125-
126148
let nextChildren = nextProps.children;
127-
reconcileChildren(current, workInProgress, nextChildren);
128-
workInProgress.memoizedProps = nextProps;
149+
reconcileChildren(
150+
current,
151+
workInProgress,
152+
nextChildren,
153+
renderExpirationTime,
154+
);
129155
return workInProgress.child;
130156
}
131157

132-
export function updateClassComponent(
133-
current,
134-
workInProgress,
135-
renderExpirationTime,
136-
) {
158+
function updateClassComponent(current, workInProgress, renderExpirationTime) {
137159
let shouldUpdate;
138160
if (current == null) {
139161
if (!workInProgress.stateNode) {
@@ -153,7 +175,12 @@ export function updateClassComponent(
153175
renderExpirationTime,
154176
);
155177
}
156-
return finishClassComponent(current, workInProgress, shouldUpdate);
178+
return finishClassComponent(
179+
current,
180+
workInProgress,
181+
shouldUpdate,
182+
renderExpirationTime,
183+
);
157184
}
158185

159186
function updateClassInstance(current, workInProgress, renderExpirationTime) {
@@ -191,28 +218,63 @@ function updateClassInstance(current, workInProgress, renderExpirationTime) {
191218
return true;
192219
}
193220

194-
function finishClassComponent(current, workInProgress, shouldUpdate) {
221+
function finishClassComponent(
222+
current,
223+
workInProgress,
224+
shouldUpdate,
225+
renderExpirationTime,
226+
) {
195227
if (!shouldUpdate) {
196-
return bailoutOnAlreadyFinishedWork(current, workInProgress);
228+
return bailoutOnAlreadyFinishedWork(
229+
current,
230+
workInProgress,
231+
renderExpirationTime,
232+
);
197233
}
198234

199235
const instance = workInProgress.stateNode;
200236
const nextChildren = instance.render();
201237

202238
workInProgress.effectTag |= PerformedWork;
203-
reconcileChildren(current, workInProgress, nextChildren);
239+
reconcileChildren(
240+
current,
241+
workInProgress,
242+
nextChildren,
243+
renderExpirationTime,
244+
);
204245
workInProgress.memoizedState = instance.state;
205-
workInProgress.memoizedProps = instance.props;
206246
return workInProgress.child;
207247
}
208248

209-
function bailoutOnAlreadyFinishedWork(current, workInProgress) {
210-
cloneChildFibers(current, workInProgress);
211-
return workInProgress.child;
249+
function bailoutOnAlreadyFinishedWork(
250+
current,
251+
workInProgress,
252+
renderExpirationTime,
253+
) {
254+
// Check if the children have any pending work.
255+
const childExpirationTime = workInProgress.childExpirationTime;
256+
if (
257+
childExpirationTime === NoWork ||
258+
childExpirationTime > renderExpirationTime
259+
) {
260+
// The children don't have any work either. We can skip them.
261+
// TODO: Once we add back resuming, we should check if the children are
262+
// a work-in-progress set. If so, we need to transfer their effects.
263+
return null;
264+
} else {
265+
// This fiber doesn't have work, but its subtree does. Clone the child
266+
// fibers and continue.
267+
cloneChildFibers(current, workInProgress);
268+
return workInProgress.child;
269+
}
212270
}
213271

214-
function reconcileChildren(current, workInProgress, nextChildren) {
215-
const renderExpirationTime = workInProgress.expirationTime;
272+
function reconcileChildren(
273+
current,
274+
workInProgress,
275+
nextChildren,
276+
renderExpirationTime,
277+
) {
216278
if (current == null) {
217279
// If this is a fresh new component that hasn't been rendered yet, we
218280
// won't update its child set by applying minimal side-effects. Instead,
@@ -240,13 +302,14 @@ function reconcileChildren(current, workInProgress, nextChildren) {
240302
}
241303
}
242304

243-
function updateMode(current, workInProgress) {
305+
function updateMode(current, workInProgress, renderExpirationTime) {
244306
const nextChildren = workInProgress.pendingProps.children;
245-
if (nextChildren == null || workInProgress.memoizedProps === nextChildren) {
246-
return bailoutOnAlreadyFinishedWork(current, workInProgress);
247-
}
248-
reconcileChildren(current, workInProgress, nextChildren);
249-
workInProgress.memoizedProps = nextChildren;
307+
reconcileChildren(
308+
current,
309+
workInProgress,
310+
nextChildren,
311+
renderExpirationTime,
312+
);
250313
return workInProgress.child;
251314
}
252315

src/FiberCompleteWork.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,7 @@ import {
1313
import { Never } from "./FiberExpirationTime";
1414

1515
export function completeWork(current, workInProgress, renderExpirationTime) {
16-
let newProps = workInProgress.pendingProps;
17-
if (newProps == null) {
18-
newProps = workInProgress.memoizedProps;
19-
} else if (
20-
workInProgress.expirationTime !== Never ||
21-
renderExpirationTime === Never
22-
) {
23-
// Reset the pending props, unless this was a down-prioritization.
24-
workInProgress.pendingProps = null;
25-
}
16+
const newProps = workInProgress.pendingProps;
2617

2718
switch (workInProgress.tag) {
2819
case FunctionalComponent:

0 commit comments

Comments
 (0)