Wait for one React CSS animation to end before starting another

5.1k views Asked by At

I'm using React and the CSSTransitionGroup component to switch between two sets of members in my app's state. Its state contains the members, each of which has a flag to say whether they're a real member of not. It also contains a flag to keep track of which type of member we're viewing:

this.state = {
      viewMembers: true,
      members: [
        {
          id: 1,
          name: "Jim",
          isMember: true
        },
        {
          id: 2,
          name: "John",
          isMember: false
        }
    ]
}

In my render function I map over this array, and only show members corresponding with the view state (i.e. if viewMembers is true, I only show members where isMember is true, and vice versa). For each member I return a table <tr>. The map function is wrapped in a CSSTransitionGroup tag to fade in the table rows. This all works fine - please see working code here: jsfiddle.net/d9cfh4zg/1/.

My issue is that it currently fades in the new state before fading out the old one, giving it a slightly janky quality. How can I allow existing rows to fade out before fading in the new ones?

1

There are 1 answers

0
Gopalkrishna Narayan Prabhu On BEST ANSWER

try this:

const styles = {
    fontFamily: "sans-serif"
  };

  const CSSTransitionGroup = React.addons.CSSTransitionGroup;

  class App extends React.Component {
    constructor() {
      super();
      this.state = {
        viewMembers: true,
        members: [
          {
            id: 1,
            name: "Jim",
            isMember: true
          },
          {
            id: 2,
            name: "John",
            isMember: false
          },
          {
            id: 3,
            name: "Sarah",
            isMember: false
          },
          {
            id: 4,
            name: "Mary",
            isMember: true
          }
        ]
      };
        this.refreshState = true;
    }

    changeViewState(state) {
      this.refreshState = false;
      setTimeout(() => {

        if (state === "members") {
          this.setState({ viewMembers: true });
          if(!this.refreshState)
          {
           this.changeViewState(state);
           this.refreshState = true;
           }
        } else {
          this.setState({ viewMembers: false });
          if(!this.refreshState)
          {
            this.changeViewState(state);
            this.refreshState = true;
           }
        }
      }, 500);

    }
    render() {
      return (
        <div style={styles}>
          <h4>Members</h4>
          <ul>
            <li onClick={() => this.changeViewState("members")}>View Members</li>
            <li onClick={() => this.changeViewState("non-members")}>
              View Non-members
            </li>
          </ul>
          <MembersTable
            viewMembers={this.state.viewMembers}
            members={this.state.members}
            refreshState = {this.refreshState}

          />
        </div>
      );
    }
  }

  const MembersTable = (props) =>
    <table>
      <thead>
        <tr>
          <td>
            <strong>Id</strong>
          </td>
          <td>
            <strong>Name</strong>
          </td>
        </tr>
      </thead>
      <CSSTransitionGroup
                component="tbody"
                transitionName="fade"
                transitionAppear={true}
                transitionAppearTimeout={500}
                transitionEnterTimeout={500}
                transitionLeaveTimeout={500}
                >
        {props.members.map(member => {
          if (
            (props.viewMembers && member.isMember && props.refreshState) ||
            (!props.viewMembers && !member.isMember && props.refreshState)
          ) {
            return (
              <tr key={member.id}>
                <td>
                  {member.id}
                </td>
                <td>
                  {member.name}
                </td>
              </tr>
            );
          }
        })}
        </CSSTransitionGroup>
    </table>;

  ReactDOM.render(<App />, document.getElementById("root"));

I have added a refreshState variable and updated it in the setTimeout callback function which is added in the changeViewState function.

Here is the updated fiddle

hope this helps.