Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ yarn snap minimize --update <path>

## Version Control

This repository uses Sapling (`sl`) for version control. Sapling is similar to Mercurial: there is not staging area, but new/deleted files must be explicitlyu added/removed.
This repository uses Sapling (`sl`) for version control. Sapling is similar to Mercurial: there is not staging area, but new/deleted files must be explicitly added/removed.

```bash
# Check status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ function inferBlock(
if (handlerParam != null) {
CompilerError.invariant(state.kind(handlerParam) != null, {
reason:
'Expected catch binding to be intialized with a DeclareLocal Catch instruction',
'Expected catch binding to be initialized with a DeclareLocal Catch instruction',
loc: terminal.loc,
});
const effects: Array<AliasingEffect> = [];
Expand Down Expand Up @@ -1315,7 +1315,7 @@ class InferenceState {
#values: Map<InstructionValue, AbstractValue>;
/*
* The set of values pointed to by each identifier. This is a set
* to accomodate phi points (where a variable may have different
* to accommodate phi points (where a variable may have different
* values from different control flow paths).
*/
#variables: Map<IdentifierId, Set<InstructionValue>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
}

/*
* Validate that all scopes have properly intialized, valid mutable ranges
* Validate that all scopes have properly initialized, valid mutable ranges
* within the span of instructions for this function, ie from 1 to 1 past
* the last instruction id.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import invariant from 'invariant';
import {runBabelPluginReactCompiler} from '../Babel/RunReactCompilerBabelPlugin';
import type {Logger, LoggerEvent} from '../Entrypoint';

it('logs succesful compilation', () => {
it('logs successful compilation', () => {
const logs: [string | null, LoggerEvent][] = [];
const logger: Logger = {
logEvent(filename, event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4062,7 +4062,7 @@ export function registerSuspenseInstanceRetry(
instance.data !== SUSPENSE_PENDING_START_DATA ||
// The boundary is still in pending status but the document has finished loading
// before we could register the event handler that would have scheduled the retry
// on load so we call teh callback now.
// on load so we call the callback now.
ownerDocument.readyState !== DOCUMENT_READY_STATE_LOADING
) {
callback();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5328,7 +5328,7 @@ export function writeHoistablesForBoundary(
hoistableState.stylesheets.forEach(hasStylesToHoist);

// We don't actually want to flush any hoistables until the boundary is complete so we omit
// any further writing here. This is becuase unlike Resources, Hoistable Elements act more like
// any further writing here. This is because unlike Resources, Hoistable Elements act more like
// regular elements, each rendered element has a unique representation in the DOM. We don't want
// these elements to appear in the DOM early, before the boundary has actually completed

Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9241,7 +9241,7 @@ describe('ReactDOMFizzServer', () => {

it('should always flush the boundaries contributing the preamble regardless of their size', async () => {
const longDescription =
`I need to make this segment somewhat large because it needs to be large enought to be outlined during the initial flush. Setting the progressive chunk size to near zero isn't enough because there is a fixed minimum size that we use to avoid doing the size tracking altogether and this needs to be larger than that at least.
`I need to make this segment somewhat large because it needs to be large enough to be outlined during the initial flush. Setting the progressive chunk size to near zero isn't enough because there is a fixed minimum size that we use to avoid doing the size tracking altogether and this needs to be larger than that at least.

Unfortunately that previous paragraph wasn't quite long enough so I'll continue with some more prose and maybe throw on some repeated additional strings at the end for good measure.

Expand Down Expand Up @@ -9277,7 +9277,7 @@ Unfortunately that previous paragraph wasn't quite long enough so I'll continue

it('should track byte size of shells that may contribute to the preamble when determining if the blocking render exceeds the max size', async () => {
const longDescription =
`I need to make this segment somewhat large because it needs to be large enought to be outlined during the initial flush. Setting the progressive chunk size to near zero isn't enough because there is a fixed minimum size that we use to avoid doing the size tracking altogether and this needs to be larger than that at least.
`I need to make this segment somewhat large because it needs to be large enough to be outlined during the initial flush. Setting the progressive chunk size to near zero isn't enough because there is a fixed minimum size that we use to avoid doing the size tracking altogether and this needs to be larger than that at least.

Unfortunately that previous paragraph wasn't quite long enough so I'll continue with some more prose and maybe throw on some repeated additional strings at the end for good measure.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ describe('ReactDOMServerHydration', () => {
}

// @TODO changes made to sending Fizz errors to client led to the insertion of templates in client rendered
// suspense boundaries. This leaks in this test becuase the client rendered suspense boundary appears like
// suspense boundaries. This leaks in this test because the client rendered suspense boundary appears like
// unhydrated tail nodes and this template is the first match. When we add special case handling for client
// rendered suspense boundaries this test will likely change again
expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -1258,7 +1258,7 @@ function completeWork(
markUpdate(workInProgress);
}
} else {
// We use the updateHostComponent path becuase it produces
// We use the updateHostComponent path because it produces
// the update queue we need for Hoistables.
updateHostComponent(
current,
Expand Down
21 changes: 16 additions & 5 deletions packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,23 @@ function propagateContextChanges<T>(
renderLanes,
workInProgress,
);
if (!forcePropagateEntireTree) {
// During lazy propagation, we can defer propagating changes to
// the children, same as the consumer match above.
nextFiber = null;
// The primary children's fibers may not exist in the tree (they
// were discarded on initial mount if they suspended). However, the
// fallback children ARE in the committed tree and visible to the
// user. We need to continue propagating into the fallback subtree
// so that its context consumers are marked for re-render.
//
// The fiber structure is:
// SuspenseComponent
// → child: OffscreenComponent (primary, hidden)
// → sibling: FallbackFragment
//
// Skip the primary (hidden) subtree and jump to the fallback.
const primaryChildFragment = fiber.child;
if (primaryChildFragment !== null) {
nextFiber = primaryChildFragment.sibling;
} else {
nextFiber = fiber.child;
nextFiber = null;
}
} else {
// Traverse down.
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -3034,7 +3034,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes): RootExitStatus {
function workLoopConcurrent(nonIdle: boolean) {
// We yield every other "frame" when rendering Transition or Retries. Those are blocking
// revealing new content. The purpose of this yield is not to avoid the overhead of yielding,
// which is very low, but rather to intentionally block any frequently occuring other main
// which is very low, but rather to intentionally block any frequently occurring other main
// thread work like animations from starving our work. In other words, the purpose of this
// is to reduce the framerate of animations to 30 frames per second.
// For Idle work we yield every 5ms to keep animations going smooth.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1700,7 +1700,7 @@ describe('ReactAsyncActions', () => {
'regression: updates in an action passed to React.startTransition are batched ' +
'even if there were no updates before the first await',
async () => {
// Regression for a bug that occured in an older, too-clever-by-half
// Regression for a bug that occurred in an older, too-clever-by-half
// implementation of the isomorphic startTransition API. Now, the
// isomorphic startTransition is literally the composition of every
// reconciler instance's startTransition, so the behavior is less likely
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1037,4 +1037,80 @@ describe('ReactLazyContextPropagation', () => {
assertLog(['Result']);
expect(root).toMatchRenderedOutput('Result');
});

// @gate enableLegacyCache
it('context change propagates to Suspense fallback (memo boundary)', async () => {
// When a context change occurs above a Suspense boundary that is currently
// showing its fallback, the fallback's context consumers should re-render
// with the updated value — even if there's a memo boundary between the
// provider and the Suspense boundary that prevents the fallback element
// references from changing.
const root = ReactNoop.createRoot();
const Context = React.createContext('A');

let setContext;
function App() {
const [value, _setValue] = useState('A');
setContext = _setValue;
return (
<Context.Provider value={value}>
<MemoizedWrapper />
<Text text={value} />
</Context.Provider>
);
}

const MemoizedWrapper = React.memo(function MemoizedWrapper() {
return (
<Suspense fallback={<FallbackConsumer />}>
<AsyncChild />
</Suspense>
);
});

function FallbackConsumer() {
const value = useContext(Context);
return <Text text={'Fallback: ' + value} />;
}

function AsyncChild() {
readText('async');
return <Text text="Content" />;
}

// Initial render — primary content suspends, fallback is shown
await act(() => {
root.render(<App />);
});
assertLog([
'Suspend! [async]',
'Fallback: A',
'A',
// pre-warming
'Suspend! [async]',
]);
expect(root).toMatchRenderedOutput('Fallback: AA');

// Update context while still suspended. The fallback consumer should
// re-render with the new value.
await act(() => {
setContext('B');
});
assertLog([
// The Suspense boundary retries the primary children first
'Suspend! [async]',
'Fallback: B',
'B',
// pre-warming
'Suspend! [async]',
]);
expect(root).toMatchRenderedOutput('Fallback: BB');

// Unsuspend. The primary content should render with the latest context.
await act(async () => {
await resolveText('async');
});
assertLog(['Content']);
expect(root).toMatchRenderedOutput('ContentB');
});
});
2 changes: 1 addition & 1 deletion packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ export opaque type Request = {
trackedPostpones: null | PostponedHoles, // Gets set to non-null while we want to track postponed holes. I.e. during a prerender.
// onError is called when an error happens anywhere in the tree. It might recover.
// The return string is used in production primarily to avoid leaking internals, secondarily to save bytes.
// Returning null/undefined will cause a defualt error message in production
// Returning null/undefined will cause a default error message in production
onError: (error: mixed, errorInfo: ThrownInfo) => ?string,
// onAllReady is called when all pending task is done but it may not have flushed yet.
// This is a good time to start writing if you want only HTML and no intermediate steps.
Expand Down
Loading