Избегайте повторной отрисовки списка в React Hooks - PullRequest
1 голос
/ 25 мая 2020

У меня есть список изображений, которые отображаются при загрузке страницы с помощью функции map. Я хочу иметь возможность выбрать одно или несколько изображений и выделить их. Кроме того, заголовки выбранных изображений будут отображаться вверху страницы. Проблема, с которой я сталкиваюсь, заключается в том, что список, кажется, обновляется каждый раз, когда я выбираю изображение. Вот компонент:

export default function App() {
  const [images, setImages] = React.useState<Image[]>([]);
  const [selected, setSelected] = React.useState<boolean>(false);
  const [imageTitles, setImageTitles] = React.useState<string[]>([]);

  const handleImageSelection = (title: string, index: number) => {
    setSelected(!selected);
    const imageExists = imageTitles.indexOf(title) !== -1
    if (!imageExists) {
      setImageTitles([...imageTitles, title]);
    } else {
      setImageTitles(imageTitles.filter(str => str !== title));
    }
  }

  return (
    <div>
      {console.log(images)} // The images array renders every time
      <div>{imageTitles.map(title => <p>{title}</p>)}</div>

      <section className="images">{
        images.map(({ title, image}, index) =>
          <img
            style={{
              marginBottom: 4,
              border: imageTitles.indexOf(title) !== -1 ?
                "4px solid blue" : "", // A selected image is highlighted
              borderRadius: 16
            }}
            src={image}
            alt={image}
            onClick={() => handleImageSelection(title, index)}
          />
        )
      }</section>
    </div>
  );
}

Мне интересно, не потому ли, что я меняю размер массива imageTitles (и, следовательно, значения index) каждый раз, когда изображение выбирается / отменяется. .

Я также пробовал useCallback вот так:

const handleImageSelection = React.useCallback((title: string, index: number) => {
  setSelected(!selected);
  const imageExists = imageTitles.indexOf(title) !== -1
  if (!imageExists) {
    setImageTitles([...imageTitles, title]);
  } else {
    setImageTitles(imageTitles.filter(str => str !== title));
  }
}, [selected, imageTitles])

Но, похоже, это не помогло. Я предполагаю, потому что imagetitles меняется каждый раз.

Итак, возможно ли (по соображениям производительности) избежать повторного рендеринга списка изображений каждый раз, когда изображение выбирается / отменяется?

1 Ответ

1 голос
/ 25 мая 2020

Поскольку вы добавляете selected и imageTitles в зависимости для useCallback, useCallback будет воссоздаваться каждый раз, когда он вызывается, поскольку он сам устанавливает состояние selected и imageTitles

Решение здесь состоит в том, чтобы использовать шаблон обратного вызова setState и передать пустой массив как зависимость от useCallback

const handleImageSelection = React.useCallback((title: string, index: number) => {
  setSelected(prevSelected => !prevSelected);


    setImageTitles(prevImageTitles => {
        const imageExists = prevImageTitles.indexOf(title) !== -1;
        if (!imageExists) { 
           return [...prevImageTitles, title];
        } else {
           return prevImageTitles.filter(str => str !== title));
        }
    });

}, []);

Также обратите внимание, что список будет повторно отображаться каждый раз, когда вы устанавливаете состояние, которое является нормальным поведением. Однако React оптимизирует рендеринг и будет повторно отображать только те элементы, которые необходимо изменить. 1008 *

return (
    <div>
      <div>{imageTitles.map(title => <p key={title}>{title}</p>)}</div>

      <section className="images">{
        images.map(({ title, image}, index) =>
          <img
            key={title} // any unique value here. If you don't have anything use index
            style={{
              marginBottom: 4,
              border: imageTitles.indexOf(title) !== -1 ?
                "4px solid blue" : "", // A selected image is highlighted
              borderRadius: 16
            }}
            src={image}
            alt={image}
            onClick={() => handleImageSelection(title, index)}
          />
        )
      }</section>
    </div>
  );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...