Skip to content

Latest commit

 

History

History
271 lines (223 loc) · 6.66 KB

migration.md

File metadata and controls

271 lines (223 loc) · 6.66 KB

Migrating from v1 to v2

A few notes to help with migrating from v1 to v2

CSSTransitionGroup migration guide

The CSSTransitionGroup has been removed, and a CSSTransition component has been added and can be used with the new TransitionGroup component to accomplish the same tasks.

tl;dr;

  • transitionName -> classNames
  • transitionEnterTimeout and transitionLeaveTimeout -> timeout={{ exit, enter }}
  • transitionAppear -> appear
  • transitionEnter -> enter
  • transitionLeave -> exit

Lets take the original docs example and migrate it:

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.example-leave {
  opacity: 1;
}

.example-leave.example-leave-active {
  opacity: 0.01;
  transition: opacity 300ms ease-in;
}

And the component:

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {items: ['hello', 'world', 'click', 'me']};
    this.handleAdd = this.handleAdd.bind(this);
  }

  handleAdd() {
    const newItems = this.state.items.concat([
      prompt('Enter some text')
    ]);
    this.setState({items: newItems});
  }

  handleRemove(i) {
    let newItems = this.state.items.slice();
    newItems.splice(i, 1);
    this.setState({items: newItems});
  }

  render() {
    const items = this.state.items.map((item, i) => (
      <div key={item} onClick={() => this.handleRemove(i)}>
        {item}
      </div>
    ));

    return (
      <div>
        <button onClick={this.handleAdd}>Add Item</button>
        <CSSTransitionGroup
          transitionName="example"
          transitionEnterTimeout={500}
          transitionLeaveTimeout={300}>
          {items}
        </CSSTransitionGroup>
      </div>
    );
  }
}

The most straightforward way to migrate is to use TransitionGroup instead of CSSTransitionGroup

render() {
  const items = this.state.items.map((item, i) => (
    <div key={item} onClick={() => this.handleRemove(i)}>
      {item}
    </div>
  ));

  return (
    <div>
      <button onClick={this.handleAdd}>Add Item</button>
-      <CSSTransitionGroup
-        transitionName="example"
-        transitionEnterTimeout={500}
-        transitionLeaveTimeout={300}>
+      <TransitionGroup>
        {items}
-      </CSSTransitionGroup>
+      </TransitionGroup>
    </div>
  )
}

That doesn't get us much, since we haven't included anything to do the animation. For that we wrap each item in a CSSTransition, but first let's adjust our css.

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

-.example-leave {
+.example-exit {
  opacity: 1;
}

-.example-leave.example-leave-active {
+.example-exit.example-exit-active {
  opacity: 0.01;
  transition: opacity 300ms ease-in;
}

All we did was replace the word "leave" with "exit". Overall, v2 uses "exit" instead of "leave" to be more symmetrical, avoiding awkwardness with english tenses (like "entered" and "leaved").

Now, at last we include the CSSTransition component.

render() {
  const items = this.state.items.map((item, i) => (
+    <CSSTransition
+      key={item}
+      classNames="example"
+      timeout={{ enter: 500, exit: 300 }}
+    >
      <div onClick={() => this.handleRemove(i)}>
        {item}
      </div>
+    <CSSTransition>
  ));

  return (
    <div>
      <button onClick={this.handleAdd}>Add Item</button>
      <TransitionGroup>
        {items}
      </TransitionGroup>
    </div>
  )
}

Notice that we replaced transitionName with classNames, it has essentially the same signature. We also replaced transitionEnterTimeout and transitionLeaveTimeout with a single timeout prop that accepts an object.

Hint: If your enter and exit timeouts are the same you can use the shorthand: timeout={500}

If we wanted to make this a bit more encapsulated we could wrap up our CSSTransition into a separate component for reuse later!

const FadeTransition = (props) => (
  <CSSTransition
    {...props}
    classNames="example"
    timeout={{ enter: 500, exit: 300 }}
  />
)

And then use it like:

render() {
  const items = this.state.items.map((item, i) => (
-    <CSSTransition
-      key={item}
-      classNames="example"
-      timeout={{ enter: 500, exit: 300 }}
-    >
+    <FadeTransition>
      <div onClick={() => this.handleRemove(i)}>
        {item}
      </div>
-    <CSSTransition>
+    </FadeTransition>
  ));

  return (
    <div>
      <button onClick={this.handleAdd}>Add Item</button>
      <TransitionGroup>
        {items}
      </TransitionGroup>
    </div>
  )
}

HEY! You may not need CSSTransition at all! The lower level Transition component is very flexible and may be easier to work with for simpler or custom cases. Check out how we migrated react-bootstrap's simple transitions to v2: Collapse and Fade components.

TransitionGroup

The old TransitionGroup managed transitions through custom static lifecycle methods on its children. In v2 we removed that API in favor of requiring that TransitionGroups be used with a Transition component, and traditional prop passing to communicate with the Group.

Practically this means that TransitionGroup will inject it's children with Transition specific props that must be passed through to the Transition component in order for the transition to work.

const MyTransition = ({ children: child, ...props }) => (
  // NOTICE THE SPREAD! THIS IS REQUIRED!
  <Transition {...props}>
    {(transitionState) => React.cloneElement(child, {
      style: getStyleBasedOnTransitionState(transitionState)
    })}
  </Transition>
)

const MyList = () => (
  <TransitionGroup>
    {items.map((item) => (
      <MyTransition>{item}</MyTransition>
    )}
  </TransitionGroup>
);

Notice how MyTransition passes all props it doesn't care about to Transition?

Callbacks

As noted, child lifecycle methods have been removed. If you do need to do some work when the Transition changes from one state to another you can use the callback props.

<Transition
  {...props}
  onEnter={handleEnter}
  onEntering={handleEntering}
  onEntered={handleEntered}
  onExit={handleExit}
  onExiting={handleExiting}
  onExited={handleExited}
/>

Each callback is called with the DOM node of the transition. Notice also that there are now actually 3 states per enter/exit transition vs the original 2. See the documentation for more details.