Реакция: переходы обрезаются после повторного рендеринга элемента в массиве elements - PullRequest
0 голосов
/ 19 июня 2019

При создании списка подкачки в 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
}

Я ищу решение, более надежное, чем мой обходной путь.

Вся помощь будет оценена! :)

1 Ответ

0 голосов
/ 19 июня 2019

Я сделал три изменения в вашем коде , поэтому после замены выделенное поле остается тем же, а затем выключается:

  1. Добавлена ​​строка setSelected(index); внутрифункция подкачки.
  2. Добавлен setTimeout для того, чтобы задержать изменение цвета.
  3. Изменил key внутри цикла map на значение index, так как он долженбыть уникальным, и поэтому лучше использовать индекс.
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>;
}
...