@samitouri / QOS-React / commits / a32c727f2e

Optimize readContext for Subsequent Reads of All Bits (#13248)

This is likely the common case because individual component authors will casually call read on common contexts like the cache, or cache provider. Where as libraries like Relay only call read once per fragment and pass all observed bits at once.

Sebastian Markbåge committed Jul 21, 2018 at 20:13 UTC a32c727f2e24bc2e8b5f532420a52b1bae2f98af
1 file changed +29 -34
packages/react-reconciler/src/ReactFiberNewContext.js
+29 -34
@@ -38,14 +38,14 @@ if (__DEV__) {
38
39 let currentlyRenderingFiber: Fiber | null = null;
40 let lastContextDependency: ContextDependency<mixed> | null = null;
41 -let lastContext: ReactContext<any> | null = null;
41 +let lastContextWithAllBitsObserved: ReactContext<any> | null = null;
42
43 export function resetContextDependences(): void {
44 // This is called right before React yields execution, to ensure `readContext`
45 // cannot be called outside the render phase.
46 currentlyRenderingFiber = null;
47 lastContextDependency = null;
48 - lastContext = null;
48 + lastContextWithAllBitsObserved = null;
49 }
50
51 export function pushProvider(providerFiber: Fiber): void {
@@ -221,7 +221,7 @@ export function prepareToReadContext(
221 ): boolean {
222 currentlyRenderingFiber = workInProgress;
223 lastContextDependency = null;
224 - lastContext = null;
224 + lastContextWithAllBitsObserved = null;
225
226 const firstContextDependency = workInProgress.firstContextDependency;
227 if (firstContextDependency !== null) {
@@ -264,46 +264,41 @@ export function readContext<T>(
264 context: ReactContext<T>,
265 observedBits: void | number | boolean,
266 ): T {
267 - if (typeof observedBits !== 'number') {
268 - if (observedBits === false) {
269 - // Do not observe updates
270 - observedBits = 0;
267 + if (lastContextWithAllBitsObserved === context) {
268 + // Nothing to do. We already observe everything in this context.
269 + } else if (observedBits === false || observedBits === 0) {
270 + // Do not observe any updates.
271 + } else {
272 + let resolvedObservedBits; // Avoid deopting on observable arguments or heterogeneous types.
273 + if (
274 + typeof observedBits !== 'number' ||
275 + observedBits === maxSigned31BitInt
276 + ) {
277 + // Observe all updates.
278 + lastContextWithAllBitsObserved = ((context: any): ReactContext<mixed>);
279 + resolvedObservedBits = maxSigned31BitInt;
280 } else {
272 - // Observe all updates
273 - observedBits = maxSigned31BitInt;
281 + resolvedObservedBits = observedBits;
282 }
275 - }
283
277 - if (lastContext === null) {
278 - invariant(
279 - currentlyRenderingFiber !== null,
280 - 'Context.unstable_read(): Context can only be read while React is ' +
281 - 'rendering, e.g. inside the render method or getDerivedStateFromProps.',
282 - );
283 - // This is the first dependency in the list
284 - currentlyRenderingFiber.firstContextDependency = lastContextDependency = {
284 + let contextItem = {
285 context: ((context: any): ReactContext<mixed>),
286 - observedBits,
286 + observedBits: resolvedObservedBits,
287 next: null,
288 };
289 - lastContext = context;
290 - } else {
291 - // `lastContextDependency` is always non-null if `lastContext is.
292 - const lastDependency: ContextDependency<T> = (lastContextDependency: any);
293 - if (lastContext === context) {
294 - // Fast path. The previous context has the same type. We can reuse
295 - // the same node.
296 - lastDependency.observedBits |= observedBits;
289 +
290 + if (lastContextDependency === null) {
291 + invariant(
292 + currentlyRenderingFiber !== null,
293 + 'Context.unstable_read(): Context can only be read while React is ' +
294 + 'rendering, e.g. inside the render method or getDerivedStateFromProps.',
295 + );
296 + // This is the first dependency in the list
297 + currentlyRenderingFiber.firstContextDependency = lastContextDependency = contextItem;
298 } else {
299 // Append a new context item.
299 - lastContextDependency = lastDependency.next = {
300 - context: ((context: any): ReactContext<mixed>),
301 - observedBits,
302 - next: null,
303 - };
304 - lastContext = context;
300 + lastContextDependency = lastContextDependency.next = contextItem;
301 }
302 }
307 -
303 return isPrimaryRenderer ? context._currentValue : context._currentValue2;
304 }