@samitouri / QOS-React-2 / commits / c44c7eaa1b

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 });