main
js 1,227 lines 40.6 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {Fiber} from './ReactInternalTypes';
11 import type {Lanes} from './ReactFiberLane';
12 import type {UpdateQueue} from './ReactFiberClassUpdateQueue';
13
14 import {
15 LayoutStatic,
16 Update,
17 Snapshot,
18 MountLayoutDev,
19 } from './ReactFiberFlags';
20 import {
21 disableLegacyContext,
22 enableSchedulingProfiler,
23 } from 'shared/ReactFeatureFlags';
24 import ReactStrictModeWarnings from './ReactStrictModeWarnings';
25 import {get as getInstance, set as setInstance} from 'shared/ReactInstanceMap';
26 import shallowEqual from 'shared/shallowEqual';
27 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
28 import getComponentNameFromType from 'shared/getComponentNameFromType';
29 import assign from 'shared/assign';
30 import isArray from 'shared/isArray';
31 import {REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE} from 'shared/ReactSymbols';
32
33 import {NoMode, StrictLegacyMode, StrictEffectsMode} from './ReactTypeOfMode';
34
35 import {
36 enqueueUpdate,
37 entangleTransitions,
38 processUpdateQueue,
39 checkHasForceUpdateAfterProcessing,
40 resetHasForceUpdateBeforeProcessing,
41 createUpdate,
42 ReplaceState,
43 ForceUpdate,
44 initializeUpdateQueue,
45 cloneUpdateQueue,
46 suspendIfUpdateReadFromEntangledAsyncAction,
47 } from './ReactFiberClassUpdateQueue';
48 import {NoLanes} from './ReactFiberLane';
49 import {
50 cacheContext,
51 getMaskedContext,
52 getUnmaskedContext,
53 hasContextChanged,
54 emptyContextObject,
55 } from './ReactFiberLegacyContext';
56 import {readContext, checkIfContextChanged} from './ReactFiberNewContext';
57 import {requestUpdateLane, scheduleUpdateOnFiber} from './ReactFiberWorkLoop';
58 import {
59 markForceUpdateScheduled,
60 markStateUpdateScheduled,
61 setIsStrictModeForDevtools,
62 } from './ReactFiberDevToolsHook';
63 import {startUpdateTimerByLane} from './ReactProfilerTimer';
64
65 const fakeInternalInstance = {};
66
67 let didWarnAboutStateAssignmentForComponent;
68 let didWarnAboutUninitializedState;
69 let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
70 let didWarnAboutLegacyLifecyclesAndDerivedState;
71 let didWarnAboutUndefinedDerivedState;
72 let didWarnAboutDirectlyAssigningPropsToState;
73 let didWarnAboutContextTypeAndContextTypes;
74 let didWarnAboutContextTypes;
75 let didWarnAboutChildContextTypes;
76 let didWarnAboutInvalidateContextType;
77 let didWarnOnInvalidCallback;
78
79 if (__DEV__) {
80 didWarnAboutStateAssignmentForComponent = new Set<string>();
81 didWarnAboutUninitializedState = new Set<string>();
82 didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set<string>();
83 didWarnAboutLegacyLifecyclesAndDerivedState = new Set<string>();
84 didWarnAboutDirectlyAssigningPropsToState = new Set<string>();
85 didWarnAboutUndefinedDerivedState = new Set<string>();
86 didWarnAboutContextTypeAndContextTypes = new Set<string>();
87 didWarnAboutContextTypes = new Set<mixed>();
88 didWarnAboutChildContextTypes = new Set<mixed>();
89 didWarnAboutInvalidateContextType = new Set<string>();
90 didWarnOnInvalidCallback = new Set<string>();
91
92 Object.freeze(fakeInternalInstance);
93 }
94
95 function warnOnInvalidCallback(callback: mixed) {
96 if (__DEV__) {
97 if (callback === null || typeof callback === 'function') {
98 return;
99 }
100 // eslint-disable-next-line react-internal/safe-string-coercion
101 const key = String(callback);
102 if (!didWarnOnInvalidCallback.has(key)) {
103 didWarnOnInvalidCallback.add(key);
104 console.error(
105 'Expected the last optional `callback` argument to be a ' +
106 'function. Instead received: %s.',
107 callback,
108 );
109 }
110 }
111 }
112
113 function warnOnUndefinedDerivedState(type: any, partialState: any) {
114 if (__DEV__) {
115 if (partialState === undefined) {
116 const componentName = getComponentNameFromType(type) || 'Component';
117 if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
118 didWarnAboutUndefinedDerivedState.add(componentName);
119 console.error(
120 '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
121 'You have returned undefined.',
122 componentName,
123 );
124 }
125 }
126 }
127 }
128
129 function applyDerivedStateFromProps(
130 workInProgress: Fiber,
131 ctor: any,
132 getDerivedStateFromProps: (props: any, state: any) => any,
133 nextProps: any,
134 ) {
135 const prevState = workInProgress.memoizedState;
136 let partialState = getDerivedStateFromProps(nextProps, prevState);
137 if (__DEV__) {
138 if (workInProgress.mode & StrictLegacyMode) {
139 setIsStrictModeForDevtools(true);
140 try {
141 // Invoke the function an extra time to help detect side-effects.
142 partialState = getDerivedStateFromProps(nextProps, prevState);
143 } finally {
144 setIsStrictModeForDevtools(false);
145 }
146 }
147 warnOnUndefinedDerivedState(ctor, partialState);
148 }
149 // Merge the partial state and the previous state.
150 const memoizedState =
151 partialState === null || partialState === undefined
152 ? prevState
153 : assign({}, prevState, partialState);
154 workInProgress.memoizedState = memoizedState;
155
156 // Once the update queue is empty, persist the derived state onto the
157 // base state.
158 if (workInProgress.lanes === NoLanes) {
159 // Queue is always non-null for classes
160 const updateQueue: UpdateQueue<any> = workInProgress.updateQueue as any;
161 updateQueue.baseState = memoizedState;
162 }
163 }
164
165 const classComponentUpdater = {
166 // $FlowFixMe[missing-local-annot]
167 enqueueSetState(inst: any, payload: any, callback) {
168 const fiber = getInstance(inst);
169 const lane = requestUpdateLane(fiber);
170
171 const update = createUpdate(lane);
172 update.payload = payload;
173 if (callback !== undefined && callback !== null) {
174 if (__DEV__) {
175 warnOnInvalidCallback(callback);
176 }
177 update.callback = callback;
178 }
179
180 const root = enqueueUpdate(fiber, update, lane);
181 if (root !== null) {
182 startUpdateTimerByLane(lane, 'this.setState()', fiber);
183 scheduleUpdateOnFiber(root, fiber, lane);
184 entangleTransitions(root, fiber, lane);
185 }
186
187 if (enableSchedulingProfiler) {
188 markStateUpdateScheduled(fiber, lane);
189 }
190 },
191 enqueueReplaceState(inst: any, payload: any, callback: null) {
192 const fiber = getInstance(inst);
193 const lane = requestUpdateLane(fiber);
194
195 const update = createUpdate(lane);
196 update.tag = ReplaceState;
197 update.payload = payload;
198
199 if (callback !== undefined && callback !== null) {
200 if (__DEV__) {
201 warnOnInvalidCallback(callback);
202 }
203 update.callback = callback;
204 }
205
206 const root = enqueueUpdate(fiber, update, lane);
207 if (root !== null) {
208 startUpdateTimerByLane(lane, 'this.replaceState()', fiber);
209 scheduleUpdateOnFiber(root, fiber, lane);
210 entangleTransitions(root, fiber, lane);
211 }
212
213 if (enableSchedulingProfiler) {
214 markStateUpdateScheduled(fiber, lane);
215 }
216 },
217 // $FlowFixMe[missing-local-annot]
218 enqueueForceUpdate(inst: any, callback) {
219 const fiber = getInstance(inst);
220 const lane = requestUpdateLane(fiber);
221
222 const update = createUpdate(lane);
223 update.tag = ForceUpdate;
224
225 if (callback !== undefined && callback !== null) {
226 if (__DEV__) {
227 warnOnInvalidCallback(callback);
228 }
229 update.callback = callback;
230 }
231
232 const root = enqueueUpdate(fiber, update, lane);
233 if (root !== null) {
234 startUpdateTimerByLane(lane, 'this.forceUpdate()', fiber);
235 scheduleUpdateOnFiber(root, fiber, lane);
236 entangleTransitions(root, fiber, lane);
237 }
238
239 if (enableSchedulingProfiler) {
240 markForceUpdateScheduled(fiber, lane);
241 }
242 },
243 };
244
245 function checkShouldComponentUpdate(
246 workInProgress: Fiber,
247 ctor: any,
248 oldProps: any,
249 newProps: any,
250 oldState: any,
251 newState: any,
252 nextContext: any,
253 ) {
254 const instance = workInProgress.stateNode;
255 if (typeof instance.shouldComponentUpdate === 'function') {
256 let shouldUpdate = instance.shouldComponentUpdate(
257 newProps,
258 newState,
259 nextContext,
260 );
261 if (__DEV__) {
262 if (workInProgress.mode & StrictLegacyMode) {
263 setIsStrictModeForDevtools(true);
264 try {
265 // Invoke the function an extra time to help detect side-effects.
266 shouldUpdate = instance.shouldComponentUpdate(
267 newProps,
268 newState,
269 nextContext,
270 );
271 } finally {
272 setIsStrictModeForDevtools(false);
273 }
274 }
275 if (shouldUpdate === undefined) {
276 console.error(
277 '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
278 'boolean value. Make sure to return true or false.',
279 getComponentNameFromType(ctor) || 'Component',
280 );
281 }
282 }
283
284 return shouldUpdate;
285 }
286
287 if (ctor.prototype && ctor.prototype.isPureReactComponent) {
288 return (
289 !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
290 );
291 }
292
293 return true;
294 }
295
296 function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
297 const instance = workInProgress.stateNode;
298 if (__DEV__) {
299 const name = getComponentNameFromType(ctor) || 'Component';
300 const renderPresent = instance.render;
301
302 if (!renderPresent) {
303 if (ctor.prototype && typeof ctor.prototype.render === 'function') {
304 console.error(
305 'No `render` method found on the %s ' +
306 'instance: did you accidentally return an object from the constructor?',
307 name,
308 );
309 } else {
310 console.error(
311 'No `render` method found on the %s ' +
312 'instance: you may have forgotten to define `render`.',
313 name,
314 );
315 }
316 }
317
318 if (
319 instance.getInitialState &&
320 !instance.getInitialState.isReactClassApproved &&
321 !instance.state
322 ) {
323 console.error(
324 'getInitialState was defined on %s, a plain JavaScript class. ' +
325 'This is only supported for classes created using React.createClass. ' +
326 'Did you mean to define a state property instead?',
327 name,
328 );
329 }
330 if (
331 instance.getDefaultProps &&
332 !instance.getDefaultProps.isReactClassApproved
333 ) {
334 console.error(
335 'getDefaultProps was defined on %s, a plain JavaScript class. ' +
336 'This is only supported for classes created using React.createClass. ' +
337 'Use a static property to define defaultProps instead.',
338 name,
339 );
340 }
341 if (instance.contextType) {
342 console.error(
343 'contextType was defined as an instance property on %s. Use a static ' +
344 'property to define contextType instead.',
345 name,
346 );
347 }
348
349 if (disableLegacyContext) {
350 if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
351 didWarnAboutChildContextTypes.add(ctor);
352 console.error(
353 '%s uses the legacy childContextTypes API which was removed in React 19. ' +
354 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
355 name,
356 );
357 }
358 if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
359 didWarnAboutContextTypes.add(ctor);
360 console.error(
361 '%s uses the legacy contextTypes API which was removed in React 19. ' +
362 'Use React.createContext() with static contextType instead. ' +
363 '(https://react.dev/link/legacy-context)',
364 name,
365 );
366 }
367 } else {
368 if (instance.contextTypes) {
369 console.error(
370 'contextTypes was defined as an instance property on %s. Use a static ' +
371 'property to define contextTypes instead.',
372 name,
373 );
374 }
375
376 if (
377 ctor.contextType &&
378 ctor.contextTypes &&
379 !didWarnAboutContextTypeAndContextTypes.has(ctor)
380 ) {
381 didWarnAboutContextTypeAndContextTypes.add(ctor);
382 console.error(
383 '%s declares both contextTypes and contextType static properties. ' +
384 'The legacy contextTypes property will be ignored.',
385 name,
386 );
387 }
388 if (ctor.childContextTypes && !didWarnAboutChildContextTypes.has(ctor)) {
389 didWarnAboutChildContextTypes.add(ctor);
390 console.error(
391 '%s uses the legacy childContextTypes API which will soon be removed. ' +
392 'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
393 name,
394 );
395 }
396 if (ctor.contextTypes && !didWarnAboutContextTypes.has(ctor)) {
397 didWarnAboutContextTypes.add(ctor);
398 console.error(
399 '%s uses the legacy contextTypes API which will soon be removed. ' +
400 'Use React.createContext() with static contextType instead. ' +
401 '(https://react.dev/link/legacy-context)',
402 name,
403 );
404 }
405 }
406
407 if (typeof instance.componentShouldUpdate === 'function') {
408 console.error(
409 '%s has a method called ' +
410 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
411 'The name is phrased as a question because the function is ' +
412 'expected to return a value.',
413 name,
414 );
415 }
416 if (
417 ctor.prototype &&
418 ctor.prototype.isPureReactComponent &&
419 typeof instance.shouldComponentUpdate !== 'undefined'
420 ) {
421 console.error(
422 '%s has a method called shouldComponentUpdate(). ' +
423 'shouldComponentUpdate should not be used when extending React.PureComponent. ' +
424 'Please extend React.Component if shouldComponentUpdate is used.',
425 getComponentNameFromType(ctor) || 'A pure component',
426 );
427 }
428 if (typeof instance.componentDidUnmount === 'function') {
429 console.error(
430 '%s has a method called ' +
431 'componentDidUnmount(). But there is no such lifecycle method. ' +
432 'Did you mean componentWillUnmount()?',
433 name,
434 );
435 }
436 if (typeof instance.componentDidReceiveProps === 'function') {
437 console.error(
438 '%s has a method called ' +
439 'componentDidReceiveProps(). But there is no such lifecycle method. ' +
440 'If you meant to update the state in response to changing props, ' +
441 'use componentWillReceiveProps(). If you meant to fetch data or ' +
442 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().',
443 name,
444 );
445 }
446 if (typeof instance.componentWillRecieveProps === 'function') {
447 console.error(
448 '%s has a method called ' +
449 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
450 name,
451 );
452 }
453 if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') {
454 console.error(
455 '%s has a method called ' +
456 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
457 name,
458 );
459 }
460 const hasMutatedProps = instance.props !== newProps;
461 if (instance.props !== undefined && hasMutatedProps) {
462 console.error(
463 'When calling super() in `%s`, make sure to pass ' +
464 "up the same props that your component's constructor was passed.",
465 name,
466 );
467 }
468 if (instance.defaultProps) {
469 console.error(
470 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' +
471 ' Instead, define defaultProps as a static property on %s.',
472 name,
473 name,
474 );
475 }
476
477 if (
478 typeof instance.getSnapshotBeforeUpdate === 'function' &&
479 typeof instance.componentDidUpdate !== 'function' &&
480 !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)
481 ) {
482 didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
483 console.error(
484 '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
485 'This component defines getSnapshotBeforeUpdate() only.',
486 getComponentNameFromType(ctor),
487 );
488 }
489
490 if (typeof instance.getDerivedStateFromProps === 'function') {
491 console.error(
492 '%s: getDerivedStateFromProps() is defined as an instance method ' +
493 'and will be ignored. Instead, declare it as a static method.',
494 name,
495 );
496 }
497 if (typeof instance.getDerivedStateFromError === 'function') {
498 console.error(
499 '%s: getDerivedStateFromError() is defined as an instance method ' +
500 'and will be ignored. Instead, declare it as a static method.',
501 name,
502 );
503 }
504 if (typeof ctor.getSnapshotBeforeUpdate === 'function') {
505 console.error(
506 '%s: getSnapshotBeforeUpdate() is defined as a static method ' +
507 'and will be ignored. Instead, declare it as an instance method.',
508 name,
509 );
510 }
511 const state = instance.state;
512 if (state && (typeof state !== 'object' || isArray(state))) {
513 console.error('%s.state: must be set to an object or null', name);
514 }
515 if (
516 typeof instance.getChildContext === 'function' &&
517 typeof ctor.childContextTypes !== 'object'
518 ) {
519 console.error(
520 '%s.getChildContext(): childContextTypes must be defined in order to ' +
521 'use getChildContext().',
522 name,
523 );
524 }
525 }
526 }
527
528 function constructClassInstance(
529 workInProgress: Fiber,
530 ctor: any,
531 props: any,
532 ): any {
533 let isLegacyContextConsumer = false;
534 let unmaskedContext = emptyContextObject;
535 let context = emptyContextObject;
536 const contextType = ctor.contextType;
537
538 if (__DEV__) {
539 if ('contextType' in ctor) {
540 const isValid =
541 // Allow null for conditional declaration
542 contextType === null ||
543 (contextType !== undefined &&
544 contextType.$$typeof === REACT_CONTEXT_TYPE);
545
546 if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
547 didWarnAboutInvalidateContextType.add(ctor);
548
549 let addendum = '';
550 if (contextType === undefined) {
551 addendum =
552 ' However, it is set to undefined. ' +
553 'This can be caused by a typo or by mixing up named and default imports. ' +
554 'This can also happen due to a circular dependency, so ' +
555 'try moving the createContext() call to a separate file.';
556 } else if (typeof contextType !== 'object') {
557 addendum = ' However, it is set to a ' + typeof contextType + '.';
558 } else if (contextType.$$typeof === REACT_CONSUMER_TYPE) {
559 addendum = ' Did you accidentally pass the Context.Consumer instead?';
560 } else {
561 addendum =
562 ' However, it is set to an object with keys {' +
563 Object.keys(contextType).join(', ') +
564 '}.';
565 }
566 console.error(
567 '%s defines an invalid contextType. ' +
568 'contextType should point to the Context object returned by React.createContext().%s',
569 getComponentNameFromType(ctor) || 'Component',
570 addendum,
571 );
572 }
573 }
574 }
575
576 if (typeof contextType === 'object' && contextType !== null) {
577 context = readContext(contextType as any);
578 } else if (!disableLegacyContext) {
579 unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
580 const contextTypes = ctor.contextTypes;
581 isLegacyContextConsumer =
582 contextTypes !== null && contextTypes !== undefined;
583 context = isLegacyContextConsumer
584 ? getMaskedContext(workInProgress, unmaskedContext)
585 : emptyContextObject;
586 }
587
588 let instance = new ctor(props, context);
589 // Instantiate twice to help detect side-effects.
590 if (__DEV__) {
591 if (workInProgress.mode & StrictLegacyMode) {
592 setIsStrictModeForDevtools(true);
593 try {
594 instance = new ctor(props, context);
595 } finally {
596 setIsStrictModeForDevtools(false);
597 }
598 }
599 }
600
601 const state = (workInProgress.memoizedState =
602 instance.state !== null && instance.state !== undefined
603 ? instance.state
604 : null);
605 instance.updater = classComponentUpdater;
606 workInProgress.stateNode = instance;
607 // The instance needs access to the fiber so that it can schedule updates
608 setInstance(instance, workInProgress);
609 if (__DEV__) {
610 instance._reactInternalInstance = fakeInternalInstance;
611 }
612
613 if (__DEV__) {
614 if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
615 const componentName = getComponentNameFromType(ctor) || 'Component';
616 if (!didWarnAboutUninitializedState.has(componentName)) {
617 didWarnAboutUninitializedState.add(componentName);
618 console.error(
619 '`%s` uses `getDerivedStateFromProps` but its initial state is ' +
620 '%s. This is not recommended. Instead, define the initial state by ' +
621 'assigning an object to `this.state` in the constructor of `%s`. ' +
622 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',
623 componentName,
624 instance.state === null ? 'null' : 'undefined',
625 componentName,
626 );
627 }
628 }
629
630 // If new component APIs are defined, "unsafe" lifecycles won't be called.
631 // Warn about these lifecycles if they are present.
632 // Don't warn about react-lifecycles-compat polyfilled methods though.
633 if (
634 typeof ctor.getDerivedStateFromProps === 'function' ||
635 typeof instance.getSnapshotBeforeUpdate === 'function'
636 ) {
637 let foundWillMountName = null;
638 let foundWillReceivePropsName = null;
639 let foundWillUpdateName = null;
640 if (
641 typeof instance.componentWillMount === 'function' &&
642 instance.componentWillMount.__suppressDeprecationWarning !== true
643 ) {
644 foundWillMountName = 'componentWillMount';
645 } else if (typeof instance.UNSAFE_componentWillMount === 'function') {
646 foundWillMountName = 'UNSAFE_componentWillMount';
647 }
648 if (
649 typeof instance.componentWillReceiveProps === 'function' &&
650 instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
651 ) {
652 foundWillReceivePropsName = 'componentWillReceiveProps';
653 } else if (
654 typeof instance.UNSAFE_componentWillReceiveProps === 'function'
655 ) {
656 foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
657 }
658 if (
659 typeof instance.componentWillUpdate === 'function' &&
660 instance.componentWillUpdate.__suppressDeprecationWarning !== true
661 ) {
662 foundWillUpdateName = 'componentWillUpdate';
663 } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
664 foundWillUpdateName = 'UNSAFE_componentWillUpdate';
665 }
666 if (
667 foundWillMountName !== null ||
668 foundWillReceivePropsName !== null ||
669 foundWillUpdateName !== null
670 ) {
671 const componentName = getComponentNameFromType(ctor) || 'Component';
672 const newApiName =
673 typeof ctor.getDerivedStateFromProps === 'function'
674 ? 'getDerivedStateFromProps()'
675 : 'getSnapshotBeforeUpdate()';
676 if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
677 didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
678 console.error(
679 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
680 '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
681 'The above lifecycles should be removed. Learn more about this warning here:\n' +
682 'https://react.dev/link/unsafe-component-lifecycles',
683 componentName,
684 newApiName,
685 foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
686 foundWillReceivePropsName !== null
687 ? `\n ${foundWillReceivePropsName}`
688 : '',
689 foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
690 );
691 }
692 }
693 }
694 }
695
696 // Cache unmasked context so we can avoid recreating masked context unless necessary.
697 // ReactFiberLegacyContext usually updates this cache but can't for newly-created instances.
698 if (isLegacyContextConsumer) {
699 cacheContext(workInProgress, unmaskedContext, context);
700 }
701
702 return instance;
703 }
704
705 function callComponentWillMount(workInProgress: Fiber, instance: any) {
706 const oldState = instance.state;
707
708 if (typeof instance.componentWillMount === 'function') {
709 instance.componentWillMount();
710 }
711 if (typeof instance.UNSAFE_componentWillMount === 'function') {
712 instance.UNSAFE_componentWillMount();
713 }
714
715 if (oldState !== instance.state) {
716 if (__DEV__) {
717 console.error(
718 '%s.componentWillMount(): Assigning directly to this.state is ' +
719 "deprecated (except inside a component's " +
720 'constructor). Use setState instead.',
721 getComponentNameFromFiber(workInProgress) || 'Component',
722 );
723 }
724 classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
725 }
726 }
727
728 function callComponentWillReceiveProps(
729 workInProgress: Fiber,
730 instance: any,
731 newProps: any,
732 nextContext: any,
733 ) {
734 const oldState = instance.state;
735 if (typeof instance.componentWillReceiveProps === 'function') {
736 instance.componentWillReceiveProps(newProps, nextContext);
737 }
738 if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
739 instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
740 }
741
742 if (instance.state !== oldState) {
743 if (__DEV__) {
744 const componentName =
745 getComponentNameFromFiber(workInProgress) || 'Component';
746 if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
747 didWarnAboutStateAssignmentForComponent.add(componentName);
748 console.error(
749 '%s.componentWillReceiveProps(): Assigning directly to ' +
750 "this.state is deprecated (except inside a component's " +
751 'constructor). Use setState instead.',
752 componentName,
753 );
754 }
755 }
756 classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
757 }
758 }
759
760 // Invokes the mount life-cycles on a previously never rendered instance.
761 function mountClassInstance(
762 workInProgress: Fiber,
763 ctor: any,
764 newProps: any,
765 renderLanes: Lanes,
766 ): void {
767 if (__DEV__) {
768 checkClassInstance(workInProgress, ctor, newProps);
769 }
770
771 const instance = workInProgress.stateNode;
772 instance.props = newProps;
773 instance.state = workInProgress.memoizedState;
774 instance.refs = {};
775
776 initializeUpdateQueue(workInProgress);
777
778 const contextType = ctor.contextType;
779 if (typeof contextType === 'object' && contextType !== null) {
780 instance.context = readContext(contextType);
781 } else if (disableLegacyContext) {
782 instance.context = emptyContextObject;
783 } else {
784 const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
785 instance.context = getMaskedContext(workInProgress, unmaskedContext);
786 }
787
788 if (__DEV__) {
789 if (instance.state === newProps) {
790 const componentName = getComponentNameFromType(ctor) || 'Component';
791 if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
792 didWarnAboutDirectlyAssigningPropsToState.add(componentName);
793 console.error(
794 '%s: It is not recommended to assign props directly to state ' +
795 "because updates to props won't be reflected in state. " +
796 'In most cases, it is better to use props directly.',
797 componentName,
798 );
799 }
800 }
801
802 if (workInProgress.mode & StrictLegacyMode) {
803 ReactStrictModeWarnings.recordLegacyContextWarning(
804 workInProgress,
805 instance,
806 );
807 }
808
809 ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
810 workInProgress,
811 instance,
812 );
813 }
814
815 instance.state = workInProgress.memoizedState;
816
817 const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
818 if (typeof getDerivedStateFromProps === 'function') {
819 applyDerivedStateFromProps(
820 workInProgress,
821 ctor,
822 getDerivedStateFromProps,
823 newProps,
824 );
825 instance.state = workInProgress.memoizedState;
826 }
827
828 // In order to support react-lifecycles-compat polyfilled components,
829 // Unsafe lifecycles should not be invoked for components using the new APIs.
830 if (
831 typeof ctor.getDerivedStateFromProps !== 'function' &&
832 typeof instance.getSnapshotBeforeUpdate !== 'function' &&
833 (typeof instance.UNSAFE_componentWillMount === 'function' ||
834 typeof instance.componentWillMount === 'function')
835 ) {
836 callComponentWillMount(workInProgress, instance);
837 // If we had additional state updates during this life-cycle, let's
838 // process them now.
839 processUpdateQueue(workInProgress, newProps, instance, renderLanes);
840 suspendIfUpdateReadFromEntangledAsyncAction();
841 instance.state = workInProgress.memoizedState;
842 }
843
844 if (typeof instance.componentDidMount === 'function') {
845 workInProgress.flags |= Update | LayoutStatic;
846 }
847 if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
848 workInProgress.flags |= MountLayoutDev;
849 }
850 }
851
852 function resumeMountClassInstance(
853 workInProgress: Fiber,
854 ctor: any,
855 newProps: any,
856 renderLanes: Lanes,
857 ): boolean {
858 const instance = workInProgress.stateNode;
859
860 const unresolvedOldProps = workInProgress.memoizedProps;
861 const oldProps = resolveClassComponentProps(ctor, unresolvedOldProps);
862 instance.props = oldProps;
863
864 const oldContext = instance.context;
865 const contextType = ctor.contextType;
866 let nextContext = emptyContextObject;
867 if (typeof contextType === 'object' && contextType !== null) {
868 nextContext = readContext(contextType);
869 } else if (!disableLegacyContext) {
870 const nextLegacyUnmaskedContext = getUnmaskedContext(
871 workInProgress,
872 ctor,
873 true,
874 );
875 nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
876 }
877
878 const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
879 const hasNewLifecycles =
880 typeof getDerivedStateFromProps === 'function' ||
881 typeof instance.getSnapshotBeforeUpdate === 'function';
882
883 // When comparing whether props changed, we should compare using the
884 // unresolved props object that is stored on the fiber, rather than the
885 // one that gets assigned to the instance, because that object may have been
886 // cloned to resolve default props and/or remove `ref`.
887 const unresolvedNewProps = workInProgress.pendingProps;
888 const didReceiveNewProps = unresolvedNewProps !== unresolvedOldProps;
889
890 // Note: During these life-cycles, instance.props/instance.state are what
891 // ever the previously attempted to render - not the "current". However,
892 // during componentDidUpdate we pass the "current" props.
893
894 // In order to support react-lifecycles-compat polyfilled components,
895 // Unsafe lifecycles should not be invoked for components using the new APIs.
896 if (
897 !hasNewLifecycles &&
898 (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
899 typeof instance.componentWillReceiveProps === 'function')
900 ) {
901 if (didReceiveNewProps || oldContext !== nextContext) {
902 callComponentWillReceiveProps(
903 workInProgress,
904 instance,
905 newProps,
906 nextContext,
907 );
908 }
909 }
910
911 resetHasForceUpdateBeforeProcessing();
912
913 const oldState = workInProgress.memoizedState;
914 let newState = (instance.state = oldState);
915 processUpdateQueue(workInProgress, newProps, instance, renderLanes);
916 suspendIfUpdateReadFromEntangledAsyncAction();
917 newState = workInProgress.memoizedState;
918 if (
919 !didReceiveNewProps &&
920 oldState === newState &&
921 !hasContextChanged() &&
922 !checkHasForceUpdateAfterProcessing()
923 ) {
924 // If an update was already in progress, we should schedule an Update
925 // effect even though we're bailing out, so that cWU/cDU are called.
926 if (typeof instance.componentDidMount === 'function') {
927 workInProgress.flags |= Update | LayoutStatic;
928 }
929 if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
930 workInProgress.flags |= MountLayoutDev;
931 }
932 return false;
933 }
934
935 if (typeof getDerivedStateFromProps === 'function') {
936 applyDerivedStateFromProps(
937 workInProgress,
938 ctor,
939 getDerivedStateFromProps,
940 newProps,
941 );
942 newState = workInProgress.memoizedState;
943 }
944
945 const shouldUpdate =
946 checkHasForceUpdateAfterProcessing() ||
947 checkShouldComponentUpdate(
948 workInProgress,
949 ctor,
950 oldProps,
951 newProps,
952 oldState,
953 newState,
954 nextContext,
955 );
956
957 if (shouldUpdate) {
958 // In order to support react-lifecycles-compat polyfilled components,
959 // Unsafe lifecycles should not be invoked for components using the new APIs.
960 if (
961 !hasNewLifecycles &&
962 (typeof instance.UNSAFE_componentWillMount === 'function' ||
963 typeof instance.componentWillMount === 'function')
964 ) {
965 if (typeof instance.componentWillMount === 'function') {
966 instance.componentWillMount();
967 }
968 if (typeof instance.UNSAFE_componentWillMount === 'function') {
969 instance.UNSAFE_componentWillMount();
970 }
971 }
972 if (typeof instance.componentDidMount === 'function') {
973 workInProgress.flags |= Update | LayoutStatic;
974 }
975 if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
976 workInProgress.flags |= MountLayoutDev;
977 }
978 } else {
979 // If an update was already in progress, we should schedule an Update
980 // effect even though we're bailing out, so that cWU/cDU are called.
981 if (typeof instance.componentDidMount === 'function') {
982 workInProgress.flags |= Update | LayoutStatic;
983 }
984 if (__DEV__ && (workInProgress.mode & StrictEffectsMode) !== NoMode) {
985 workInProgress.flags |= MountLayoutDev;
986 }
987
988 // If shouldComponentUpdate returned false, we should still update the
989 // memoized state to indicate that this work can be reused.
990 workInProgress.memoizedProps = newProps;
991 workInProgress.memoizedState = newState;
992 }
993
994 // Update the existing instance's state, props, and context pointers even
995 // if shouldComponentUpdate returns false.
996 instance.props = newProps;
997 instance.state = newState;
998 instance.context = nextContext;
999
1000 return shouldUpdate;
1001 }
1002
1003 // Invokes the update life-cycles and returns false if it shouldn't rerender.
1004 function updateClassInstance(
1005 current: Fiber,
1006 workInProgress: Fiber,
1007 ctor: any,
1008 newProps: any,
1009 renderLanes: Lanes,
1010 ): boolean {
1011 const instance = workInProgress.stateNode;
1012
1013 cloneUpdateQueue(current, workInProgress);
1014
1015 const unresolvedOldProps = workInProgress.memoizedProps;
1016 const oldProps = resolveClassComponentProps(ctor, unresolvedOldProps);
1017 instance.props = oldProps;
1018 const unresolvedNewProps = workInProgress.pendingProps;
1019
1020 const oldContext = instance.context;
1021 const contextType = ctor.contextType;
1022 let nextContext = emptyContextObject;
1023 if (typeof contextType === 'object' && contextType !== null) {
1024 nextContext = readContext(contextType);
1025 } else if (!disableLegacyContext) {
1026 const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
1027 nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
1028 }
1029
1030 const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
1031 const hasNewLifecycles =
1032 typeof getDerivedStateFromProps === 'function' ||
1033 typeof instance.getSnapshotBeforeUpdate === 'function';
1034
1035 // Note: During these life-cycles, instance.props/instance.state are what
1036 // ever the previously attempted to render - not the "current". However,
1037 // during componentDidUpdate we pass the "current" props.
1038
1039 // In order to support react-lifecycles-compat polyfilled components,
1040 // Unsafe lifecycles should not be invoked for components using the new APIs.
1041 if (
1042 !hasNewLifecycles &&
1043 (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
1044 typeof instance.componentWillReceiveProps === 'function')
1045 ) {
1046 if (
1047 unresolvedOldProps !== unresolvedNewProps ||
1048 oldContext !== nextContext
1049 ) {
1050 callComponentWillReceiveProps(
1051 workInProgress,
1052 instance,
1053 newProps,
1054 nextContext,
1055 );
1056 }
1057 }
1058
1059 resetHasForceUpdateBeforeProcessing();
1060
1061 const oldState = workInProgress.memoizedState;
1062 let newState = (instance.state = oldState);
1063 processUpdateQueue(workInProgress, newProps, instance, renderLanes);
1064 suspendIfUpdateReadFromEntangledAsyncAction();
1065 newState = workInProgress.memoizedState;
1066
1067 if (
1068 unresolvedOldProps === unresolvedNewProps &&
1069 oldState === newState &&
1070 !hasContextChanged() &&
1071 !checkHasForceUpdateAfterProcessing() &&
1072 !(
1073 // prettier-ignore
1074 // $FlowFixMe[invalid-compare]
1075 (current !== null &&
1076 current.dependencies !== null &&
1077 checkIfContextChanged(current.dependencies))
1078 )
1079 ) {
1080 // If an update was already in progress, we should schedule an Update
1081 // effect even though we're bailing out, so that cWU/cDU are called.
1082 if (typeof instance.componentDidUpdate === 'function') {
1083 if (
1084 unresolvedOldProps !== current.memoizedProps ||
1085 oldState !== current.memoizedState
1086 ) {
1087 workInProgress.flags |= Update;
1088 }
1089 }
1090 if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1091 if (
1092 unresolvedOldProps !== current.memoizedProps ||
1093 oldState !== current.memoizedState
1094 ) {
1095 workInProgress.flags |= Snapshot;
1096 }
1097 }
1098 return false;
1099 }
1100
1101 if (typeof getDerivedStateFromProps === 'function') {
1102 applyDerivedStateFromProps(
1103 workInProgress,
1104 ctor,
1105 getDerivedStateFromProps,
1106 newProps,
1107 );
1108 newState = workInProgress.memoizedState;
1109 }
1110
1111 const shouldUpdate =
1112 checkHasForceUpdateAfterProcessing() ||
1113 checkShouldComponentUpdate(
1114 workInProgress,
1115 ctor,
1116 oldProps,
1117 newProps,
1118 oldState,
1119 newState,
1120 nextContext,
1121 ) ||
1122 // TODO: In some cases, we'll end up checking if context has changed twice,
1123 // both before and after `shouldComponentUpdate` has been called. Not ideal,
1124 // but I'm loath to refactor this function. This only happens for memoized
1125 // components so it's not that common.
1126 // $FlowFixMe[invalid-compare]
1127 (current !== null &&
1128 current.dependencies !== null &&
1129 checkIfContextChanged(current.dependencies));
1130
1131 if (shouldUpdate) {
1132 // In order to support react-lifecycles-compat polyfilled components,
1133 // Unsafe lifecycles should not be invoked for components using the new APIs.
1134 if (
1135 !hasNewLifecycles &&
1136 (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
1137 typeof instance.componentWillUpdate === 'function')
1138 ) {
1139 if (typeof instance.componentWillUpdate === 'function') {
1140 instance.componentWillUpdate(newProps, newState, nextContext);
1141 }
1142 if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
1143 instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
1144 }
1145 }
1146 if (typeof instance.componentDidUpdate === 'function') {
1147 workInProgress.flags |= Update;
1148 }
1149 if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1150 workInProgress.flags |= Snapshot;
1151 }
1152 } else {
1153 // If an update was already in progress, we should schedule an Update
1154 // effect even though we're bailing out, so that cWU/cDU are called.
1155 if (typeof instance.componentDidUpdate === 'function') {
1156 if (
1157 unresolvedOldProps !== current.memoizedProps ||
1158 oldState !== current.memoizedState
1159 ) {
1160 workInProgress.flags |= Update;
1161 }
1162 }
1163 if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1164 if (
1165 unresolvedOldProps !== current.memoizedProps ||
1166 oldState !== current.memoizedState
1167 ) {
1168 workInProgress.flags |= Snapshot;
1169 }
1170 }
1171
1172 // If shouldComponentUpdate returned false, we should still update the
1173 // memoized props/state to indicate that this work can be reused.
1174 workInProgress.memoizedProps = newProps;
1175 workInProgress.memoizedState = newState;
1176 }
1177
1178 // Update the existing instance's state, props, and context pointers even
1179 // if shouldComponentUpdate returns false.
1180 instance.props = newProps;
1181 instance.state = newState;
1182 instance.context = nextContext;
1183
1184 return shouldUpdate;
1185 }
1186
1187 export function resolveClassComponentProps(
1188 Component: any,
1189 baseProps: Object,
1190 ): Object {
1191 let newProps = baseProps;
1192
1193 // Remove ref from the props object, if it exists.
1194 if ('ref' in baseProps) {
1195 newProps = {} as any;
1196 for (const propName in baseProps) {
1197 if (propName !== 'ref') {
1198 newProps[propName] = baseProps[propName];
1199 }
1200 }
1201 }
1202
1203 // Resolve default props.
1204 const defaultProps = Component.defaultProps;
1205 if (defaultProps) {
1206 // We may have already copied the props object above to remove ref. If so,
1207 // we can modify that. Otherwise, copy the props object with Object.assign.
1208 if (newProps === baseProps) {
1209 newProps = assign({}, newProps);
1210 }
1211 // Taken from old JSX runtime, where this used to live.
1212 for (const propName in defaultProps) {
1213 if (newProps[propName] === undefined) {
1214 newProps[propName] = defaultProps[propName];
1215 }
1216 }
1217 }
1218
1219 return newProps;
1220 }
1221
1222 export {
1223 constructClassInstance,
1224 mountClassInstance,
1225 resumeMountClassInstance,
1226 updateClassInstance,
1227 };