首页 > 解决方案 > React:在元素数组中进一步重新渲染元素后,转换被切断

问题描述

在 React 中创建交换列表时,我遇到了一个奇怪的转换错误。列表的工作方式很简单:单击一个元素 (A),它的背景过渡到较浅的颜色,然后单击另一个 (B),它们交换位置,元素 A 现在位于新位置,过渡回原色。

至少当元素 A 的索引比元素 B 高时会发生这种情况。反之,交换后的转换被切断。

我设法使用 找到了解决方法window.requestAnimationFrame,但它并不完美。过渡的状态不会被保留,这意味着它总是从完整的浅色过渡回来。在这里没那么重要,但这在我的另一个项目中是一个问题。有时过渡也会被剪掉。

代码很简单。需要注意的是元素在交换后保留了它们的键。我创建了一个代码沙箱供您玩耍。

import React, { useState } from "react";

const Item = props => {
  let classes = "item";

  if (props.selected) {
    classes += " selected";
  }

  return (
    <div className={classes} onClick={props.onClick}>
      {props.value}
    </div>
  );
};

const App = () => {
  const [list, setList] = useState([1, 2, 3]);
  const [selected, setSelected] = useState(-1);

  const select = index => {
    setSelected(index);
  };

  const swap = index => {
    const newList = [...list];
    [newList[index], newList[selected]] = [newList[selected], newList[index]];
    setList(newList);
    setSelected(-1);
  };

  // The workaround that kind of works, but is not perfect.
  // const swap = index => {
  //   const newList = [...list];
  //   [newList[index], newList[selected]] = [newList[selected], newList[index]];
  //   setList(newList);

  //   window.requestAnimationFrame(() => {
  //     setSelected(index);
  //     window.requestAnimationFrame(() => {
  //       setSelected(-1);
  //     });
  //   });
  // };

  const onClick = selected < 0 ? select : swap;

  const items = list.map((value, index) => (
    <Item
      key={value}
      value={value}
      selected={selected === index}
      onClick={onClick.bind(this, index)}
    />
  ));

  return <div className="list">{items}</div>;
}

以下是关键的 CSS 规则:

.item {
  background: #0b7189; // darker
  transition: background-color 1s;
}

.item.selected {
  background-color: #228cdb; //lighter
}

我正在寻找比我的解决方法更可靠的解决方案。

所有帮助将不胜感激!:)

标签: javascriptcssreactjscss-transitions

解决方案


我在您的代码中进行了三处更改,因此在交换突出显示的框时保持不变,然后关闭:

  1. setSelected(index);在 swap 函数中添加了这一行。
  2. 添加了一个setTimeout以延迟颜色变化。
  3. 将循环key内部更改为值,因为它必须是唯一的,因此使用索引只是一个更好的做法。mapindex
function App() {
  const [list, setList] = useState([1, 2, 3]);
  const [selected, setSelected] = useState(-1);

  const select = index => {
    setSelected(index);
  };

  const swap = index => {
    const newList = [...list];
    [newList[index], newList[selected]] = [newList[selected], newList[index]];
    setList(newList);

    // This was added in order to keep the highlight after swap.
    // Note that it takes a second to take place
    setSelected(index);

    // And this was added in order to allow the colour to lighten,
    // before returning to the original colour
    setTimeout(() => setSelected(-1), 1000);
  };

  const onClick = selected < 0 ? select : swap;

  const items = list.map((value, index) => (
    <Item
      key={index}
      value={value}
      selected={selected === index}
      onClick={onClick.bind(this, index)}
    />
  ));

  return <div className="list">{items}</div>;
}

推荐阅读