Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit bfb6568

Browse filesBrowse files
authored
experimental_use(context)(#25202)
1 parent a9dc73c commit bfb6568
Copy full SHA for bfb6568

4 files changed

+141-111Lines changed: 141 additions & 111 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/react-reconciler/src/ReactFiberHooks.new.js‎

Copy file name to clipboardExpand all lines: packages/react-reconciler/src/ReactFiberHooks.new.js
+58-55Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
enableUseHook,
3737
enableUseMemoCacheHook,
3838
} from 'shared/ReactFeatureFlags';
39+
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
3940

4041
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
4142
import {
@@ -714,68 +715,70 @@ function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
714715
}
715716

716717
function use<T>(usable: Usable<T>): T {
717-
if (
718-
usable !== null &&
719-
typeof usable === 'object' &&
720-
typeof usable.then === 'function'
721-
) {
722-
// This is a thenable.
723-
const thenable: Thenable<T> = (usable: any);
724-
725-
// Track the position of the thenable within this fiber.
726-
const index = thenableIndexCounter;
727-
thenableIndexCounter += 1;
728-
729-
switch (thenable.status) {
730-
case 'fulfilled': {
731-
const fulfilledValue: T = thenable.value;
732-
return fulfilledValue;
733-
}
734-
case 'rejected': {
735-
const rejectedError = thenable.reason;
736-
throw rejectedError;
737-
}
738-
default: {
739-
const prevThenableAtIndex: Thenable<T> | null = getPreviouslyUsedThenableAtIndex(
740-
index,
741-
);
742-
if (prevThenableAtIndex !== null) {
743-
switch (prevThenableAtIndex.status) {
744-
case 'fulfilled': {
745-
const fulfilledValue: T = prevThenableAtIndex.value;
746-
return fulfilledValue;
747-
}
748-
case 'rejected': {
749-
const rejectedError: mixed = prevThenableAtIndex.reason;
750-
throw rejectedError;
751-
}
752-
default: {
753-
// The thenable still hasn't resolved. Suspend with the same
754-
// thenable as last time to avoid redundant listeners.
755-
throw prevThenableAtIndex;
718+
if (usable !== null && typeof usable === 'object') {
719+
if (typeof usable.then === 'function') {
720+
// This is a thenable.
721+
const thenable: Thenable<T> = (usable: any);
722+
723+
// Track the position of the thenable within this fiber.
724+
const index = thenableIndexCounter;
725+
thenableIndexCounter += 1;
726+
727+
switch (thenable.status) {
728+
case 'fulfilled': {
729+
const fulfilledValue: T = thenable.value;
730+
return fulfilledValue;
731+
}
732+
case 'rejected': {
733+
const rejectedError = thenable.reason;
734+
throw rejectedError;
735+
}
736+
default: {
737+
const prevThenableAtIndex: Thenable<T> | null = getPreviouslyUsedThenableAtIndex(
738+
index,
739+
);
740+
if (prevThenableAtIndex !== null) {
741+
switch (prevThenableAtIndex.status) {
742+
case 'fulfilled': {
743+
const fulfilledValue: T = prevThenableAtIndex.value;
744+
return fulfilledValue;
745+
}
746+
case 'rejected': {
747+
const rejectedError: mixed = prevThenableAtIndex.reason;
748+
throw rejectedError;
749+
}
750+
default: {
751+
// The thenable still hasn't resolved. Suspend with the same
752+
// thenable as last time to avoid redundant listeners.
753+
throw prevThenableAtIndex;
754+
}
756755
}
756+
} else {
757+
// This is the first time something has been used at this index.
758+
// Stash the thenable at the current index so we can reuse it during
759+
// the next attempt.
760+
trackUsedThenable(thenable, index);
761+
762+
// Suspend.
763+
// TODO: Throwing here is an implementation detail that allows us to
764+
// unwind the call stack. But we shouldn't allow it to leak into
765+
// userspace. Throw an opaque placeholder value instead of the
766+
// actual thenable. If it doesn't get captured by the work loop, log
767+
// a warning, because that means something in userspace must have
768+
// caught it.
769+
throw thenable;
757770
}
758-
} else {
759-
// This is the first time something has been used at this index.
760-
// Stash the thenable at the current index so we can reuse it during
761-
// the next attempt.
762-
trackUsedThenable(thenable, index);
763-
764-
// Suspend.
765-
// TODO: Throwing here is an implementation detail that allows us to
766-
// unwind the call stack. But we shouldn't allow it to leak into
767-
// userspace. Throw an opaque placeholder value instead of the
768-
// actual thenable. If it doesn't get captured by the work loop, log
769-
// a warning, because that means something in userspace must have
770-
// caught it.
771-
throw thenable;
772771
}
773772
}
773+
} else if (
774+
usable.$$typeof != null &&
775+
usable.$$typeof === REACT_CONTEXT_TYPE
776+
) {
777+
const context: ReactContext<T> = (usable: any);
778+
return readContext(context);
774779
}
775780
}
776781

777-
// TODO: Add support for Context
778-
779782
// eslint-disable-next-line react-internal/safe-string-coercion
780783
throw new Error('An unsupported type was passed to use(): ' + String(usable));
781784
}
Collapse file

‎packages/react-reconciler/src/ReactFiberHooks.old.js‎

Copy file name to clipboardExpand all lines: packages/react-reconciler/src/ReactFiberHooks.old.js
+58-55Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
enableUseHook,
3737
enableUseMemoCacheHook,
3838
} from 'shared/ReactFeatureFlags';
39+
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
3940

4041
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
4142
import {
@@ -714,68 +715,70 @@ function createFunctionComponentUpdateQueue(): FunctionComponentUpdateQueue {
714715
}
715716

716717
function use<T>(usable: Usable<T>): T {
717-
if (
718-
usable !== null &&
719-
typeof usable === 'object' &&
720-
typeof usable.then === 'function'
721-
) {
722-
// This is a thenable.
723-
const thenable: Thenable<T> = (usable: any);
724-
725-
// Track the position of the thenable within this fiber.
726-
const index = thenableIndexCounter;
727-
thenableIndexCounter += 1;
728-
729-
switch (thenable.status) {
730-
case 'fulfilled': {
731-
const fulfilledValue: T = thenable.value;
732-
return fulfilledValue;
733-
}
734-
case 'rejected': {
735-
const rejectedError = thenable.reason;
736-
throw rejectedError;
737-
}
738-
default: {
739-
const prevThenableAtIndex: Thenable<T> | null = getPreviouslyUsedThenableAtIndex(
740-
index,
741-
);
742-
if (prevThenableAtIndex !== null) {
743-
switch (prevThenableAtIndex.status) {
744-
case 'fulfilled': {
745-
const fulfilledValue: T = prevThenableAtIndex.value;
746-
return fulfilledValue;
747-
}
748-
case 'rejected': {
749-
const rejectedError: mixed = prevThenableAtIndex.reason;
750-
throw rejectedError;
751-
}
752-
default: {
753-
// The thenable still hasn't resolved. Suspend with the same
754-
// thenable as last time to avoid redundant listeners.
755-
throw prevThenableAtIndex;
718+
if (usable !== null && typeof usable === 'object') {
719+
if (typeof usable.then === 'function') {
720+
// This is a thenable.
721+
const thenable: Thenable<T> = (usable: any);
722+
723+
// Track the position of the thenable within this fiber.
724+
const index = thenableIndexCounter;
725+
thenableIndexCounter += 1;
726+
727+
switch (thenable.status) {
728+
case 'fulfilled': {
729+
const fulfilledValue: T = thenable.value;
730+
return fulfilledValue;
731+
}
732+
case 'rejected': {
733+
const rejectedError = thenable.reason;
734+
throw rejectedError;
735+
}
736+
default: {
737+
const prevThenableAtIndex: Thenable<T> | null = getPreviouslyUsedThenableAtIndex(
738+
index,
739+
);
740+
if (prevThenableAtIndex !== null) {
741+
switch (prevThenableAtIndex.status) {
742+
case 'fulfilled': {
743+
const fulfilledValue: T = prevThenableAtIndex.value;
744+
return fulfilledValue;
745+
}
746+
case 'rejected': {
747+
const rejectedError: mixed = prevThenableAtIndex.reason;
748+
throw rejectedError;
749+
}
750+
default: {
751+
// The thenable still hasn't resolved. Suspend with the same
752+
// thenable as last time to avoid redundant listeners.
753+
throw prevThenableAtIndex;
754+
}
756755
}
756+
} else {
757+
// This is the first time something has been used at this index.
758+
// Stash the thenable at the current index so we can reuse it during
759+
// the next attempt.
760+
trackUsedThenable(thenable, index);
761+
762+
// Suspend.
763+
// TODO: Throwing here is an implementation detail that allows us to
764+
// unwind the call stack. But we shouldn't allow it to leak into
765+
// userspace. Throw an opaque placeholder value instead of the
766+
// actual thenable. If it doesn't get captured by the work loop, log
767+
// a warning, because that means something in userspace must have
768+
// caught it.
769+
throw thenable;
757770
}
758-
} else {
759-
// This is the first time something has been used at this index.
760-
// Stash the thenable at the current index so we can reuse it during
761-
// the next attempt.
762-
trackUsedThenable(thenable, index);
763-
764-
// Suspend.
765-
// TODO: Throwing here is an implementation detail that allows us to
766-
// unwind the call stack. But we shouldn't allow it to leak into
767-
// userspace. Throw an opaque placeholder value instead of the
768-
// actual thenable. If it doesn't get captured by the work loop, log
769-
// a warning, because that means something in userspace must have
770-
// caught it.
771-
throw thenable;
772771
}
773772
}
773+
} else if (
774+
usable.$$typeof != null &&
775+
usable.$$typeof === REACT_CONTEXT_TYPE
776+
) {
777+
const context: ReactContext<T> = (usable: any);
778+
return readContext(context);
774779
}
775780
}
776781

777-
// TODO: Add support for Context
778-
779782
// eslint-disable-next-line react-internal/safe-string-coercion
780783
throw new Error('An unsupported type was passed to use(): ' + String(usable));
781784
}
Collapse file

‎packages/react-reconciler/src/__tests__/ReactWakeable-test.js‎

Copy file name to clipboardExpand all lines: packages/react-reconciler/src/__tests__/ReactWakeable-test.js
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,28 @@ describe('ReactWakeable', () => {
315315
]);
316316
expect(root).toMatchRenderedOutput('Caught an error: Oops!');
317317
});
318+
319+
// @gate enableUseHook
320+
test('basic use(context)', () => {
321+
const ContextA = React.createContext('');
322+
const ContextB = React.createContext('B');
323+
324+
function Sync() {
325+
const text = use(ContextA) + use(ContextB);
326+
return text;
327+
}
328+
329+
function App() {
330+
return (
331+
<ContextA.Provider value="A">
332+
<Sync />
333+
</ContextA.Provider>
334+
);
335+
}
336+
337+
const root = ReactNoop.createRoot();
338+
root.render(<App />);
339+
expect(Scheduler).toFlushWithoutYielding();
340+
expect(root).toMatchRenderedOutput('AB');
341+
});
318342
});
Collapse file

‎packages/shared/ReactTypes.js‎

Copy file name to clipboardExpand all lines: packages/shared/ReactTypes.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,4 @@ export type StartTransitionOptions = {
214214
};
215215

216216
// TODO: Add Context support
217-
export type Usable<T> = Thenable<T>;
217+
export type Usable<T> = Thenable<T> | ReactContext<T>;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.