Fire componentDidMount/componentDidUpdate life-cycles
These happen in the commit phase *before* the setState callback. Unfortunately, we've lost the previous state at this point since we've already mutated the instance. This needs to be fixed somehow.
Sebastian Markbage committed
Oct 7, 2016 at 14:25 UTC
c44c7eaa1b8c6d1e628505adff2866176c0b1d84
3 files changed
+101
-4
src/renderers/shared/fiber/ReactFiberCommitWork.js
+16
-2
@@ -213,6 +213,20 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
213
function commitWork(current : ?Fiber, finishedWork : Fiber) : void {
214
switch (finishedWork.tag) {
215
case ClassComponent: {
216
+ const instance = finishedWork.stateNode;
217
+ if (!current) {
218
+ if (typeof instance.componentDidMount === 'function') {
219
+ instance.componentDidMount();
220
+ }
221
+ } else {
222
+ if (typeof instance.componentDidUpdate === 'function') {
223
+ const prevProps = current.memoizedProps;
224
+ // TODO: This is the new state. We don't currently have the previous
225
+ // state anymore.
226
+ const prevState = instance.state || null;
227
+ instance.componentDidUpdate(prevProps, prevState);
228
+ }
229
+ }
230
// Clear updates from current fiber. This must go before the callbacks
231
// are reset, in case an update is triggered from inside a callback. Is
232
// this safe? Relies on the assumption that work is only committed if
@@ -223,9 +237,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
237
if (finishedWork.callbackList) {
238
const { callbackList } = finishedWork;
239
finishedWork.callbackList = null;
226
- callCallbacks(callbackList, finishedWork.stateNode);
240
+ callCallbacks(callbackList, instance);
241
}
228
- // TODO: Fire componentDidMount/componentDidUpdate, update refs
242
+ // TODO: Fire update refs
243
return;
244
}
245
case HostContainer: {
src/renderers/shared/fiber/ReactFiberScheduler.js
+3
@@ -116,6 +116,9 @@ module.exports = function<T, P, I, TI, C>(config : HostConfig<T, P, I, TI, C>) {
116
let effectfulFiber = finishedWork.firstEffect;
117
while (effectfulFiber) {
118
switch (effectfulFiber.effectTag) {
119
+ // TODO: Should we commit host updates here? Otherwise update to parent
120
+ // host components are not visible in life-cycles. Such as when you read
121
+ // layout information.
122
case Placement:
123
case PlacementAndUpdate:
124
commitInsertion(effectfulFiber);
src/renderers/shared/fiber/__tests__/ReactIncrementalSideEffects-test.js
+82
-2
@@ -557,7 +557,87 @@ describe('ReactIncrementalSideEffects', () => {
557
558
});
559
560
- // TODO: Test that unmounts and deletions happen in the expected way for
561
- // aborted and resumed render life-cycles.
560
+ it('calls componentDidMount/Update after insertion/update', () => {
561
+
562
+ var ops = [];
563
+
564
+ class Bar extends React.Component {
565
+ componentDidMount() {
566
+ ops.push('mount:' + this.props.name);
567
+ }
568
+ componentDidUpdate() {
569
+ ops.push('update:' + this.props.name);
570
+ }
571
+ render() {
572
+ return <span />;
573
+ }
574
+ }
575
+
576
+ class Wrapper extends React.Component {
577
+ componentDidMount() {
578
+ ops.push('mount:wrapper-' + this.props.name);
579
+ }
580
+ componentDidUpdate() {
581
+ ops.push('update:wrapper-' + this.props.name);
582
+ }
583
+ render() {
584
+ return <Bar name={this.props.name} />;
585
+ }
586
+ }
587
+
588
+ function Foo(props) {
589
+ return (
590
+ <div>
591
+ <Bar key="a" name="A" />
592
+ <Wrapper key="b" name="B" />
593
+ <div key="cd">
594
+ <Bar name="C" />
595
+ <Wrapper name="D" />
596
+ </div>
597
+ {[
598
+ <Bar key="e" name="E" />,
599
+ <Bar key="f" name="F" />,
600
+ ]}
601
+ <div>
602
+ <Bar key="g" name="G" />
603
+ </div>
604
+ </div>
605
+ );
606
+ }
607
+
608
+ ReactNoop.render(<Foo />);
609
+ ReactNoop.flush();
610
+ expect(ops).toEqual([
611
+ 'mount:A',
612
+ 'mount:B',
613
+ 'mount:wrapper-B',
614
+ 'mount:C',
615
+ 'mount:D',
616
+ 'mount:wrapper-D',
617
+ 'mount:E',
618
+ 'mount:F',
619
+ 'mount:G',
620
+ ]);
621
+
622
+ ops = [];
623
+
624
+ ReactNoop.render(<Foo />);
625
+ ReactNoop.flush();
626
+ expect(ops).toEqual([
627
+ 'update:A',
628
+ 'update:B',
629
+ 'update:wrapper-B',
630
+ 'update:C',
631
+ 'update:D',
632
+ 'update:wrapper-D',
633
+ 'update:E',
634
+ 'update:F',
635
+ 'update:G',
636
+ ]);
637
+
638
+ });
639
+
640
+ // TODO: Test that mounts, updates, refs, unmounts and deletions happen in the
641
+ // expected way for aborted and resumed render life-cycles.
642
643
});