简体   繁体   中英

Element values shifting and changing before transition with react-transition-group

I noticed this behavior while using the react-transition-group package in a gatsby project I'm working on. I have a list of "tags" that are added to an active list as they are picked from another master list. Clicking a tag from the master list adds it to the active list, and clicking a tag from the active list removes it. Pretty much how you would expect something like this to work.

The transition in works just fine, but when transitioning out the tags re-organize themselves in a strange way. We have five tags with the following values:

  • Dairy Free
  • Party Food
  • Family Sized
  • Low Cholesterol
  • Low Sodium

If you click the Family Sized tag to remove it, the following occurs:

  1. Family Sized disappears instantly
  2. Low Cholesterol and Low Sodium shift instantly to the left
  3. The last tag changes to Low Sodium instantly
  4. The last tag, now with the value of Low Sodium transitions out

The expected behavior is that the Family Sized tag transitions out from where it is in the middle of the group. I should note that it works fine if you remove the last tag from the active list, this only happens when removing a tag from any other position.

Here is a slowed-down GIF of the transition and here is my code:

<TransitionGroup className='tag-container'>
  {filter.map((tag, index) => {
    return (
      <CSSTransition key={index} timeout={250} classNames={`tag`}>
        <TagButton value={tag} onClick={onClickFunction} className={`${screenStyle} active`}>{tag}</TagButton>
      </CSSTransition>
    )
  })}
</TransitionGroup>
  • filter is an array destructured from the component's state.
  • There is a <StaticQuery> from gatsby used in the same render() method of the component if that matters.
  • The <TagButton> is a styled component from styled-components package, not a separately imported component.

Adding specific identifier in your array and setting key of each element to each identifier in map method will solve your problem.

import React from "react";
import ReactDOM from "react-dom";
import { Button } from "react-bootstrap";
import { CSSTransition, TransitionGroup } from "react-transition-group";
import "bootstrap/dist/css/bootstrap.css";
import "./styles.css";
import uuid from "uuid";

function App() {
  const [items, setitems] = React.useState([
    { id: uuid(), name: "Dairy Free" },
    { id: uuid(), name: "Party Food" },
    { id: uuid(), name: "Family Sized" },
    { id: uuid(), name: "Low Cholesterol" },
    { id: uuid(), name: "Low Sodium" }
  ]);
  const removeitem = item => {
    setitems(items.filter(itemname => itemname.id !== item));
  };
  return (
    <div className="App">
      <TransitionGroup className="todo-list">
        {items.map(data => (
          <CSSTransition timeout={500} classNames="item" key={data.id}>
            <Button className="m-2" onClick={() => removeitem(data.id)}>
              {data.name}
            </Button>
          </CSSTransition>
        ))}
      </TransitionGroup>
    </div>
  );
}

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

This problem is already solved here , so please checkout to understand this strange behavior.

For future reference if anyone comes across this. The issue was with how the key value was being passed to the tags.

My code was mapping through the filter array and assigning the index of each value as the child components' keys. This means they are keyed as 0, 1, 2, 3, etc. When a tag is removed from the filter array and the page re-renders, the indexes are re-applied in whatever order the array is in, which means that they all shift instead of the one that was removed being "plucked out" from wherever it was.

The solution was to use a unique key that doesn't rely on the array's index values at all. Here is what I updated to:

<TransitionGroup className='tag-container'>
  {filter.map((tag) => {
    return (
      <CSSTransition key={`active_${tag}`} timeout={250} classNames={`tag`}>
        <TagButton value={tag} onClick={onClickFunction} className={`${screenStyle} active`}>{tag}</TagButton>
      </CSSTransition>
    )
  })}
</TransitionGroup>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM