Prepare new composite child before removing old (#8572)

This matches what we do in Fiber -- and doing it this way is the only way we can prepare new views in the background before unmounting old ones.

In particular, this breaks this pattern:

```js
class Child1 extends React.Component {
  render() { ... }
  componentWillMount() {
    this.props.registerChild(this);
  }
  componentWillUnmount() {
    this.props.unregisterChild();
  }
}

class Child2 extends React.Component {
  render() { ... }
  componentWillMount() {
    this.props.registerChild(this);
  }
  componentWillUnmount() {
    this.props.unregisterChild();
  }
}

class Parent extends React.Component {
  render() {
    return (
      showChild1 ?
        <Child1
          registerChild={(child) => this.registered = child}
          unregisterChild={() => this.registered = null}
        /> :
        <Child2
          registerChild={(child) => this.registered = child}
          unregisterChild={() => this.registered = null}
        />
    );
  }
}
```

Previously, `this.registered` would always be set -- now, after a rerender, `this.registered` gets stuck at null because the old child's componentWillUnmount runs *after* the new child's componentWillMount.

A correct fix here is to use componentDidMount rather than componentWillMount. (In general, componentWillMount should not have side effects.) If Parent stored a list or set of registered children instead, there would also be no issue.
This commit is contained in:
Ben Alpert
2016-12-14 11:14:50 -08:00
committed by GitHub
parent 931cad5aae
commit ba8f24ba99
7 changed files with 155 additions and 8 deletions
+2
View File
@@ -1318,6 +1318,7 @@ src/renderers/shared/shared/__tests__/ReactCompositeComponent-test.js
* should support objects with prototypes as state
* should not warn about unmounting during unmounting
* should only call componentWillUnmount once
* prepares new child before unmounting old
src/renderers/shared/shared/__tests__/ReactCompositeComponentDOMMinimalism-test.js
* should not render extra nodes for non-interpolated text
@@ -1405,6 +1406,7 @@ src/renderers/shared/shared/__tests__/ReactMultiChild-test.js
* should NOT replace children with different owners
* should replace children with different keys
* should reorder bailed-out children
* prepares new children before unmounting old
src/renderers/shared/shared/__tests__/ReactMultiChildReconcile-test.js
* should reset internal state if removed then readded in an array