Skip to content

Commit 06531e0

Browse files
committed
New context API
Introduces a declarative context API that propagates updates even when shouldComponentUpdate returns false.
1 parent 6e258c1 commit 06531e0

10 files changed

+866
-20
lines changed

packages/react-reconciler/src/ReactChildFiber.js

+215-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
*/
99

1010
import type {ReactElement} from 'shared/ReactElementType';
11-
import type {ReactCall, ReactPortal, ReactReturn} from 'shared/ReactTypes';
11+
import type {
12+
ReactCall,
13+
ReactPortal,
14+
ReactReturn,
15+
ReactProvider,
16+
ReactConsumer,
17+
} from 'shared/ReactTypes';
1218
import type {Fiber} from 'react-reconciler/src/ReactFiber';
1319
import type {ExpirationTime} from 'react-reconciler/src/ReactFiberExpirationTime';
1420

@@ -20,6 +26,8 @@ import {
2026
REACT_CALL_TYPE,
2127
REACT_RETURN_TYPE,
2228
REACT_PORTAL_TYPE,
29+
REACT_PROVIDER_TYPE,
30+
REACT_CONSUMER_TYPE,
2331
} from 'shared/ReactSymbols';
2432
import {
2533
FunctionalComponent,
@@ -29,6 +37,8 @@ import {
2937
CallComponent,
3038
ReturnComponent,
3139
Fragment,
40+
ProviderComponent,
41+
ConsumerComponent,
3242
} from 'shared/ReactTypeOfWork';
3343
import emptyObject from 'fbjs/lib/emptyObject';
3444
import invariant from 'fbjs/lib/invariant';
@@ -42,6 +52,8 @@ import {
4252
createFiberFromCall,
4353
createFiberFromReturn,
4454
createFiberFromPortal,
55+
createFiberFromProvider,
56+
createFiberFromConsumer,
4557
} from './ReactFiber';
4658
import ReactDebugCurrentFiber from './ReactDebugCurrentFiber';
4759

@@ -465,6 +477,52 @@ function ChildReconciler(shouldTrackSideEffects) {
465477
}
466478
}
467479

480+
function updateProviderComponent(
481+
returnFiber: Fiber,
482+
current: Fiber | null,
483+
provider: ReactProvider<any>,
484+
expirationTime: ExpirationTime,
485+
) {
486+
if (current !== null && current.type === provider.context) {
487+
// Move based on index
488+
const existing = useFiber(current, provider, expirationTime);
489+
existing.return = returnFiber;
490+
return existing;
491+
} else {
492+
// Insert
493+
const created = createFiberFromProvider(
494+
provider,
495+
returnFiber.internalContextTag,
496+
expirationTime,
497+
);
498+
created.return = returnFiber;
499+
return created;
500+
}
501+
}
502+
503+
function updateConsumerComponent(
504+
returnFiber: Fiber,
505+
current: Fiber | null,
506+
consumer: ReactConsumer<any>,
507+
expirationTime: ExpirationTime,
508+
) {
509+
if (current !== null && current.type === consumer.context) {
510+
// Move based on index
511+
const existing = useFiber(current, consumer, expirationTime);
512+
existing.return = returnFiber;
513+
return existing;
514+
} else {
515+
// Insert
516+
const created = createFiberFromConsumer(
517+
consumer,
518+
returnFiber.internalContextTag,
519+
expirationTime,
520+
);
521+
created.return = returnFiber;
522+
return created;
523+
}
524+
}
525+
468526
function createChild(
469527
returnFiber: Fiber,
470528
newChild: any,
@@ -537,6 +595,24 @@ function ChildReconciler(shouldTrackSideEffects) {
537595
created.return = returnFiber;
538596
return created;
539597
}
598+
case REACT_PROVIDER_TYPE: {
599+
const created = createFiberFromProvider(
600+
newChild,
601+
returnFiber.internalContextTag,
602+
expirationTime,
603+
);
604+
created.return = returnFiber;
605+
return created;
606+
}
607+
case REACT_CONSUMER_TYPE: {
608+
const created = createFiberFromConsumer(
609+
newChild,
610+
returnFiber.internalContextTag,
611+
expirationTime,
612+
);
613+
created.return = returnFiber;
614+
return created;
615+
}
540616
}
541617

542618
if (isArray(newChild) || getIteratorFn(newChild)) {
@@ -647,6 +723,30 @@ function ChildReconciler(shouldTrackSideEffects) {
647723
return null;
648724
}
649725
}
726+
case REACT_PROVIDER_TYPE: {
727+
if (newChild.key === key) {
728+
return updateProviderComponent(
729+
returnFiber,
730+
oldFiber,
731+
newChild,
732+
expirationTime,
733+
);
734+
} else {
735+
return null;
736+
}
737+
}
738+
case REACT_CONSUMER_TYPE: {
739+
if (newChild.key === key) {
740+
return updateConsumerComponent(
741+
returnFiber,
742+
oldFiber,
743+
newChild,
744+
expirationTime,
745+
);
746+
} else {
747+
return null;
748+
}
749+
}
650750
}
651751

652752
if (isArray(newChild) || getIteratorFn(newChild)) {
@@ -755,6 +855,30 @@ function ChildReconciler(shouldTrackSideEffects) {
755855
expirationTime,
756856
);
757857
}
858+
case REACT_PROVIDER_TYPE: {
859+
const matchedFiber =
860+
existingChildren.get(
861+
newChild.key === null ? newIdx : newChild.key,
862+
) || null;
863+
return updateProviderComponent(
864+
returnFiber,
865+
matchedFiber,
866+
newChild,
867+
expirationTime,
868+
);
869+
}
870+
case REACT_CONSUMER_TYPE: {
871+
const matchedFiber =
872+
existingChildren.get(
873+
newChild.key === null ? newIdx : newChild.key,
874+
) || null;
875+
return updateConsumerComponent(
876+
returnFiber,
877+
matchedFiber,
878+
newChild,
879+
expirationTime,
880+
);
881+
}
758882
}
759883

760884
if (isArray(newChild) || getIteratorFn(newChild)) {
@@ -1362,6 +1486,78 @@ function ChildReconciler(shouldTrackSideEffects) {
13621486
return created;
13631487
}
13641488

1489+
function reconcileSingleProvider(
1490+
returnFiber: Fiber,
1491+
currentFirstChild: Fiber | null,
1492+
provider: ReactProvider<any>,
1493+
expirationTime: ExpirationTime,
1494+
): Fiber {
1495+
const key = provider.key;
1496+
let child = currentFirstChild;
1497+
while (child !== null) {
1498+
// TODO: If key === null and child.key === null, then this only applies to
1499+
// the first item in the list.
1500+
if (child.key === key && child.type === provider.context) {
1501+
if (child.tag === ProviderComponent) {
1502+
deleteRemainingChildren(returnFiber, child.sibling);
1503+
const existing = useFiber(child, provider, expirationTime);
1504+
existing.return = returnFiber;
1505+
return existing;
1506+
} else {
1507+
deleteRemainingChildren(returnFiber, child);
1508+
break;
1509+
}
1510+
} else {
1511+
deleteChild(returnFiber, child);
1512+
}
1513+
child = child.sibling;
1514+
}
1515+
1516+
const created = createFiberFromProvider(
1517+
provider,
1518+
returnFiber.internalContextTag,
1519+
expirationTime,
1520+
);
1521+
created.return = returnFiber;
1522+
return created;
1523+
}
1524+
1525+
function reconcileSingleConsumer(
1526+
returnFiber: Fiber,
1527+
currentFirstChild: Fiber | null,
1528+
consumer: ReactConsumer<any>,
1529+
expirationTime: ExpirationTime,
1530+
): Fiber {
1531+
const key = consumer.key;
1532+
let child = currentFirstChild;
1533+
while (child !== null) {
1534+
// TODO: If key === null and child.key === null, then this only applies to
1535+
// the first item in the list.
1536+
if (child.key === key && child.type === consumer.context) {
1537+
if (child.tag === ConsumerComponent) {
1538+
deleteRemainingChildren(returnFiber, child.sibling);
1539+
const existing = useFiber(child, consumer, expirationTime);
1540+
existing.return = returnFiber;
1541+
return existing;
1542+
} else {
1543+
deleteRemainingChildren(returnFiber, child);
1544+
break;
1545+
}
1546+
} else {
1547+
deleteChild(returnFiber, child);
1548+
}
1549+
child = child.sibling;
1550+
}
1551+
1552+
const created = createFiberFromConsumer(
1553+
consumer,
1554+
returnFiber.internalContextTag,
1555+
expirationTime,
1556+
);
1557+
created.return = returnFiber;
1558+
return created;
1559+
}
1560+
13651561
// This API will tag the children with the side-effect of the reconciliation
13661562
// itself. They will be added to the side-effect list as we pass through the
13671563
// children and the parent.
@@ -1430,6 +1626,24 @@ function ChildReconciler(shouldTrackSideEffects) {
14301626
expirationTime,
14311627
),
14321628
);
1629+
case REACT_PROVIDER_TYPE:
1630+
return placeSingleChild(
1631+
reconcileSingleProvider(
1632+
returnFiber,
1633+
currentFirstChild,
1634+
newChild,
1635+
expirationTime,
1636+
),
1637+
);
1638+
case REACT_CONSUMER_TYPE:
1639+
return placeSingleChild(
1640+
reconcileSingleConsumer(
1641+
returnFiber,
1642+
currentFirstChild,
1643+
newChild,
1644+
expirationTime,
1645+
),
1646+
);
14331647
}
14341648
}
14351649

packages/react-reconciler/src/ReactFiber.js

+38
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import type {
1212
ReactFragment,
1313
ReactPortal,
1414
ReactReturn,
15+
ReactProvider,
16+
ReactConsumer,
1517
} from 'shared/ReactTypes';
1618
import type {TypeOfWork} from 'shared/ReactTypeOfWork';
1719
import type {TypeOfInternalContext} from './ReactTypeOfInternalContext';
@@ -31,6 +33,8 @@ import {
3133
CallComponent,
3234
ReturnComponent,
3335
Fragment,
36+
ProviderComponent,
37+
ConsumerComponent,
3438
} from 'shared/ReactTypeOfWork';
3539
import getComponentName from 'shared/getComponentName';
3640

@@ -442,3 +446,37 @@ export function createFiberFromPortal(
442446
};
443447
return fiber;
444448
}
449+
450+
export function createFiberFromProvider<T>(
451+
provider: ReactProvider<T>,
452+
internalContextTag: TypeOfInternalContext,
453+
expirationTime: ExpirationTime,
454+
): Fiber {
455+
const pendingProps = provider;
456+
const fiber = createFiber(
457+
ProviderComponent,
458+
pendingProps,
459+
provider.key,
460+
internalContextTag,
461+
);
462+
fiber.expirationTime = expirationTime;
463+
fiber.type = provider.context;
464+
return fiber;
465+
}
466+
467+
export function createFiberFromConsumer<T>(
468+
consumer: ReactConsumer<T>,
469+
internalContextTag: TypeOfInternalContext,
470+
expirationTime: ExpirationTime,
471+
): Fiber {
472+
const pendingProps = consumer;
473+
const fiber = createFiber(
474+
ConsumerComponent,
475+
pendingProps,
476+
consumer.key,
477+
internalContextTag,
478+
);
479+
fiber.expirationTime = expirationTime;
480+
fiber.type = consumer.context;
481+
return fiber;
482+
}

0 commit comments

Comments
 (0)