14
import {
15
debugRenderPhaseSideEffects,
16
enableAsyncSubtreeAPI,
17
+ warnAboutDeprecatedLifecycles,
18
} from 'shared/ReactFeatureFlags';
19
import {isMounted} from 'react-reconciler/reflection';
20
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
41
const fakeInternalInstance = {};
42
const isArray = Array.isArray;
43
44
+let didWarnAboutLegacyWillMount;
45
+let didWarnAboutLegacyWillReceiveProps;
46
+let didWarnAboutLegacyWillUpdate;
47
let didWarnAboutStateAssignmentForComponent;
48
+let didWarnAboutUndefinedDerivedState;
49
+let didWarnAboutUninitializedState;
50
+let didWarnAboutWillReceivePropsAndDerivedState;
51
let warnOnInvalidCallback;
52
53
if (__DEV__) {
47
- const didWarnOnInvalidCallback = {};
54
+ if (warnAboutDeprecatedLifecycles) {
55
+ didWarnAboutLegacyWillMount = {};
56
+ didWarnAboutLegacyWillReceiveProps = {};
57
+ didWarnAboutLegacyWillUpdate = {};
58
+ }
59
didWarnAboutStateAssignmentForComponent = {};
60
+ didWarnAboutUndefinedDerivedState = {};
61
+ didWarnAboutUninitializedState = {};
62
+ didWarnAboutWillReceivePropsAndDerivedState = {};
63
+
64
+ const didWarnOnInvalidCallback = {};
65
66
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
67
if (callback === null || typeof callback === 'function') {
335
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
336
name,
337
);
338
+ const noUnsafeComponentWillRecieveProps =
339
+ typeof instance.UNSAFE_componentWillRecieveProps !== 'function';
340
+ warning(
341
+ noUnsafeComponentWillRecieveProps,
342
+ '%s has a method called ' +
343
+ 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
344
+ name,
345
+ );
346
const hasMutatedProps = instance.props !== workInProgress.pendingProps;
347
warning(
348
instance.props === undefined || !hasMutatedProps,
402
? getMaskedContext(workInProgress, unmaskedContext)
403
: emptyObject;
404
const instance = new ctor(props, context);
405
+ const state =
406
+ instance.state !== null && instance.state !== undefined
407
+ ? instance.state
408
+ : null;
409
adoptClassInstance(workInProgress, instance);
410
411
+ if (__DEV__) {
412
+ if (
413
+ typeof ctor.getDerivedStateFromProps === 'function' &&
414
+ state === null
415
+ ) {
416
+ const componentName = getComponentName(workInProgress) || 'Unknown';
417
+ if (!didWarnAboutUninitializedState[componentName]) {
418
+ warning(
419
+ false,
420
+ '%s: Did not properly initialize state during construction. ' +
421
+ 'Expected state to be an object, but it was %s.',
422
+ componentName,
423
+ instance.state === null ? 'null' : 'undefined',
424
+ );
425
+ didWarnAboutUninitializedState[componentName] = true;
426
+ }
427
+ }
428
+ }
429
+
430
+ workInProgress.memoizedState = state;
431
+
432
+ const partialState = callGetDerivedStateFromProps(
433
+ workInProgress,
434
+ instance,
435
+ props,
436
+ );
437
+
438
+ if (partialState !== null && partialState !== undefined) {
439
+ // Render-phase updates (like this) should not be added to the update queue,
440
+ // So that multiple render passes do not enqueue multiple updates.
441
+ // Instead, just synchronously merge the returned state into the instance.
442
+ workInProgress.memoizedState = Object.assign(
443
+ {},
444
+ workInProgress.memoizedState,
445
+ partialState,
446
+ );
447
+ }
448
+
449
// Cache unmasked context so we can avoid recreating masked context unless necessary.
450
// ReactFiberContext usually updates this cache but can't for newly-created instances.
451
if (needsContext) {
458
function callComponentWillMount(workInProgress, instance) {
459
startPhaseTimer(workInProgress, 'componentWillMount');
460
const oldState = instance.state;
395
- instance.componentWillMount();
461
+
462
+ if (typeof instance.componentWillMount === 'function') {
463
+ if (__DEV__) {
464
+ if (warnAboutDeprecatedLifecycles) {
465
+ const componentName = getComponentName(workInProgress) || 'Component';
466
+ if (!didWarnAboutLegacyWillMount[componentName]) {
467
+ warning(
468
+ false,
469
+ '%s: componentWillMount() is deprecated and will be ' +
470
+ 'removed in the next major version. Read about the motivations ' +
471
+ 'behind this change: ' +
472
+ 'https://fb.me/react-async-component-lifecycle-hooks' +
473
+ '\n\n' +
474
+ 'As a temporary workaround, you can rename to ' +
475
+ 'UNSAFE_componentWillMount instead.',
476
+ componentName,
477
+ );
478
+ didWarnAboutLegacyWillMount[componentName] = true;
479
+ }
480
+ }
481
+ }
482
+ instance.componentWillMount();
483
+ } else {
484
+ instance.UNSAFE_componentWillMount();
485
+ }
486
+
487
stopPhaseTimer();
488
489
if (oldState !== instance.state) {
506
newProps,
507
newContext,
508
) {
418
- startPhaseTimer(workInProgress, 'componentWillReceiveProps');
509
const oldState = instance.state;
420
- instance.componentWillReceiveProps(newProps, newContext);
421
- stopPhaseTimer();
510
+ if (typeof instance.componentWillReceiveProps === 'function') {
511
+ if (__DEV__) {
512
+ if (warnAboutDeprecatedLifecycles) {
513
+ const componentName = getComponentName(workInProgress) || 'Component';
514
+ if (!didWarnAboutLegacyWillReceiveProps[componentName]) {
515
+ warning(
516
+ false,
517
+ '%s: componentWillReceiveProps() is deprecated and ' +
518
+ 'will be removed in the next major version. Use ' +
519
+ 'static getDerivedStateFromProps() instead. Read about the ' +
520
+ 'motivations behind this change: ' +
521
+ 'https://fb.me/react-async-component-lifecycle-hooks' +
522
+ '\n\n' +
523
+ 'As a temporary workaround, you can rename to ' +
524
+ 'UNSAFE_componentWillReceiveProps instead.',
525
+ componentName,
526
+ );
527
+ didWarnAboutLegacyWillReceiveProps[componentName] = true;
528
+ }
529
+ }
530
+ }
531
423
- // Simulate an async bailout/interruption by invoking lifecycle twice.
424
- if (debugRenderPhaseSideEffects) {
532
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
533
instance.componentWillReceiveProps(newProps, newContext);
534
+ stopPhaseTimer();
535
+ } else {
536
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
537
+ instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
538
+ stopPhaseTimer();
539
+
540
+ // Simulate an async bailout/interruption by invoking lifecycle twice.
541
+ if (debugRenderPhaseSideEffects) {
542
+ instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
543
+ }
544
}
545
546
if (instance.state !== oldState) {
561
}
562
}
563
564
+ function callGetDerivedStateFromProps(
565
+ workInProgress: Fiber,
566
+ instance: any,
567
+ props: any,
568
+ ) {
569
+ const {type} = workInProgress;
570
+
571
+ if (typeof type.getDerivedStateFromProps === 'function') {
572
+ if (__DEV__) {
573
+ if (
574
+ typeof instance.componentWillReceiveProps === 'function' ||
575
+ typeof instance.UNSAFE_componentWillReceiveProps === 'function'
576
+ ) {
577
+ const componentName = getComponentName(workInProgress) || 'Unknown';
578
+ if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
579
+ warning(
580
+ false,
581
+ '%s: Defines both componentWillReceiveProps() and static ' +
582
+ 'getDerivedStateFromProps() methods. We recommend using ' +
583
+ 'only getDerivedStateFromProps().',
584
+ componentName,
585
+ );
586
+ didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
587
+ }
588
+ }
589
+ }
590
+
591
+ const partialState = type.getDerivedStateFromProps.call(
592
+ null,
593
+ props,
594
+ workInProgress.memoizedState,
595
+ );
596
+
597
+ if (__DEV__) {
598
+ if (partialState === undefined) {
599
+ const componentName = getComponentName(workInProgress) || 'Unknown';
600
+ if (!didWarnAboutUndefinedDerivedState[componentName]) {
601
+ warning(
602
+ false,
603
+ '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
604
+ 'You have returned undefined.',
605
+ componentName,
606
+ );
607
+ didWarnAboutUndefinedDerivedState[componentName] = componentName;
608
+ }
609
+ }
610
+ }
611
+
612
+ return partialState;
613
+ }
614
+ }
615
+
616
// Invokes the mount life-cycles on a previously never rendered instance.
617
function mountClassInstance(
618
workInProgress: Fiber,
625
}
626
627
const instance = workInProgress.stateNode;
458
- const state = instance.state || null;
628
const props = workInProgress.pendingProps;
629
const unmaskedContext = getUnmaskedContext(workInProgress);
630
631
instance.props = props;
463
- instance.state = workInProgress.memoizedState = state;
632
+ instance.state = workInProgress.memoizedState;
633
instance.refs = emptyObject;
634
instance.context = getMaskedContext(workInProgress, unmaskedContext);
635
642
workInProgress.internalContextTag |= AsyncUpdates;
643
}
644
476
- if (typeof instance.componentWillMount === 'function') {
645
+ if (
646
+ typeof instance.UNSAFE_componentWillMount === 'function' ||
647
+ typeof instance.componentWillMount === 'function'
648
+ ) {
649
callComponentWillMount(workInProgress, instance);
650
// If we had additional state updates during this life-cycle, let's
651
// process them now.
791
// during componentDidUpdate we pass the "current" props.
792
793
if (
622
- typeof instance.componentWillReceiveProps === 'function' &&
794
+ (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
795
+ typeof instance.componentWillReceiveProps === 'function') &&
796
(oldProps !== newProps || oldContext !== newContext)
797
) {
798
callComponentWillReceiveProps(
803
);
804
}
805
806
+ let partialState;
807
+ if (oldProps !== newProps) {
808
+ partialState = callGetDerivedStateFromProps(
809
+ workInProgress,
810
+ instance,
811
+ newProps,
812
+ );
813
+ }
814
+
815
// Compute the next state using the memoized state and the update queue.
816
const oldState = workInProgress.memoizedState;
817
// TODO: Previous state can be null.
829
newState = oldState;
830
}
831
832
+ if (partialState !== null && partialState !== undefined) {
833
+ // Render-phase updates (like this) should not be added to the update queue,
834
+ // So that multiple render passes do not enqueue multiple updates.
835
+ // Instead, just synchronously merge the returned state into the instance.
836
+ newState =
837
+ newState === null || newState === undefined
838
+ ? partialState
839
+ : Object.assign({}, newState, partialState);
840
+ }
841
+
842
if (
843
oldProps === newProps &&
844
oldState === newState &&
871
);
872
873
if (shouldUpdate) {
682
- if (typeof instance.componentWillUpdate === 'function') {
683
- startPhaseTimer(workInProgress, 'componentWillUpdate');
684
- instance.componentWillUpdate(newProps, newState, newContext);
685
- stopPhaseTimer();
686
-
687
- // Simulate an async bailout/interruption by invoking lifecycle twice.
688
- if (debugRenderPhaseSideEffects) {
874
+ if (
875
+ typeof instance.UNSAFE_componentWillUpdate === 'function' ||
876
+ typeof instance.componentWillUpdate === 'function'
877
+ ) {
878
+ if (typeof instance.componentWillUpdate === 'function') {
879
+ if (__DEV__) {
880
+ if (warnAboutDeprecatedLifecycles) {
881
+ const componentName =
882
+ getComponentName(workInProgress) || 'Component';
883
+ if (!didWarnAboutLegacyWillUpdate[componentName]) {
884
+ warning(
885
+ false,
886
+ '%s: componentWillUpdate() is deprecated and will be ' +
887
+ 'removed in the next major version. Read about the motivations ' +
888
+ 'behind this change: ' +
889
+ 'https://fb.me/react-async-component-lifecycle-hooks' +
890
+ '\n\n' +
891
+ 'As a temporary workaround, you can rename to ' +
892
+ 'UNSAFE_componentWillUpdate instead.',
893
+ componentName,
894
+ );
895
+ didWarnAboutLegacyWillUpdate[componentName] = true;
896
+ }
897
+ }
898
+ }
899
+
900
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
901
instance.componentWillUpdate(newProps, newState, newContext);
902
+ stopPhaseTimer();
903
+ } else {
904
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
905
+ instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
906
+ stopPhaseTimer();
907
+
908
+ // Simulate an async bailout/interruption by invoking lifecycle twice.
909
+ if (debugRenderPhaseSideEffects) {
910
+ instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
911
+ }
912
}
913
}
914
if (typeof instance.componentDidUpdate === 'function') {
943
944
return {
945
adoptClassInstance,
946
+ callGetDerivedStateFromProps,
947
constructClassInstance,
948
mountClassInstance,
949
// resumeMountClassInstance,