Как предотвратить повторную визуализацию всего списка при добавлении нового элемента в React

#reactjs

Вопрос:

Я хотел бы отображать только недавно добавленные элементы вместо повторного отображения всего списка, когда в список добавляется новый элемент. Я попытался добавить ключевую опору, но все равно все элементы отображаются. Это пример кода для моего варианта использования https://codesandbox.io/s/jovial-poincare-qcsvn?file=/src/App.js, здесь мы можем увидеть журнал консоли для всех элементов списка, когда мы добавляем новый элемент в список.

Комментарии:

1. Почему вас волнует, если функции визуализации каждого элемента вызываются снова? Производите ли вы тяжелые вычисления в какой-либо из функций рендеринга? Оказывает ли это какое-либо измеримое влияние? Выверщик проверяет опору ключа и не будет вносить никаких изменений в DOM для элементов со стабильными ключами.

2. Да, для каждого элемента списка есть довольно интенсивный рендеринг, например, несколько огромных изображений

3. Это связано с тем, что вы определили свой компонент внутри приложения, ознакомьтесь с моим ответом для получения более подробной информации.

Ответ №1:

Это происходит потому, что вы определили свой компонент commentView внутри компонента приложения любое обновление состояния приводит к повторному отображению компонента, и react обрабатывает эти воссозданные версии commentView, поскольку это совершенно другой компонент, давайте предположим, что react думает, что вы изменили div на span, он понятия не имеет, что это тот же самый старый компонент

Поднимите компонент за пределы компонента приложения

 const CommentView = React.memo(/**/)
const App = () => {/* rest without the CommentView definition */}
 

вы также можете использовать чистый компонент вместо памятки, если хотите, которые сравниваются поверхностно

 class CommentView extends PureComponent {
  render() {
    const comment = this.props.comment;
    return (
      <h1 key={`comment-${comment.id}`}>
        {console.log("Rendering", comment.id)}
        {comment.id}: {comment.msg}
      </h1>
    );
  }
}

export default function App() {
// rest of code
 

Комментарии:

1. Потрясающе, это сработало как заклинание, спасибо.

Ответ №2:

Во-первых, ваш идентификатор не уникален. Если вы добавите это, оно будет уникальным:

  {
    id: comments[comments.length - 1].id   1,
    msg: "C"
 }
 

Изменение состояния означает, что React запускает обновление при вызове setState функции (в крючках React вы бы использовали useState ). Это не только означает, что будет вызвана функция визуализации компонента, но также и то, что все его последующие дочерние компоненты будут повторно отрисованы, независимо от того, изменились их реквизиты или нет.

React.memo только поверхностное сравнение.

Поэтому вам нужно указать свою собственную логику сравнения в качестве второго параметра.

 function MyComponent(props) {
  /* render using props */
}

function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}

export default React.memo(MyComponent, areEqual);
 

Комментарии:

1. Нет, похоже, это происходит не из-за одного и того же ключа. по крайней мере, он должен правильно работать в первом добавлении, которое имеет уникальный ключ «c»

2. Попытался добавить функцию сравнения в песочницу, но эта функция даже не вызывается

3. Это именно потому, что он воссоздан вместо повторного рендеринга, ознакомьтесь с моим ответом для более подробной информации, дайте мне знать, если что-то останется неясным