У меня есть компонент React, который отображает таблицу слов и их переводы на основе данных, полученных с сервера. Слова делятся на группы, одно и то же слово может присутствовать более чем в одной группе. Каждая группа представлена отдельным тегом <tbody> в одной таблице.
У меня есть следующий маршрут REST API для удаления слова из группы: DELETE /api/groups/group_id/words/word_id. Я хочу добавить эту функциональность в свою таблицу React, чтобы каждая строка таблицы имела кнопку удаления, которая при нажатии отправляет запрос AJAX на сервер и в случае успеха удаляет строку из таблицы.
Самый семантически правильный способ удалить строку из таблицы - обновить состояние компонента, в котором хранятся данные. Что-то вроде этого:
this.setState(state => {
const groups = [...state.groups];
const groupIndex = groups.findIndex(group => group._id == group_id);
const group = groups[groupIndex] = { ...groups[groupIndex] }; // for the sake of immutability
const words = group.words = [...group.words];
const wordIndex = words.findIndex(word => word._id == word_id);
words.splice(wordIndex, 1);
return { groups };
});
Это, в свою очередь, приведет к повторному рендерингу компонента. Но здесь возникает проблема: повторный рендеринг всего компонента только для удаления одной строки таблицы может быть излишним, поскольку this.state.groups и вложенные массивы слов придется пересекать снова (поскольку метод render() вызывает метод массивов map() для построения дерево компонентов), и после этого необходимо будет вызвать согласование React. Это кажется ужасной идеей, если мы заботимся о производительности.
Я знаю, что должен избегать этого в React, насколько это возможно, но не лучше ли в данном конкретном случае вернуться к старой доброй DOM ради производительности? Вот что я имею в виду:
deleteWord = (event, group_id, word_id) => {
fetch(`/api/groups/${group_id}/words/${word_id}`, {
method: 'DELETE'
}).then(response => {
if (!response.ok) throw new Error(response.statusText);
}).then(() => {
// event.target is the delete button that has been clicked
const td = event.target.parentNode; // table cell
const tr = td.parentNode; // table row
tr.parentNode.removeChild(tr);
});
};
Я все еще мог обновлять состояние, чтобы все было синхронизировано, но с shouldComponentUpdate(), возвращающим false, чтобы избежать ненужных вычислений.
Итак, мой первый вопрос: какой путь мне выбрать? Может я ошибаюсь и разница в производительности не так критична?
Однако это не единственный вопрос, который у меня есть. Я еще ничего не сказал о том, как я собираюсь получить значения group_id и word_id при нажатии одной из кнопок удаления. И именно здесь у меня возникает другая дилемма.
Наиболее очевидный подход - добавить анонимные функции в качестве прослушивателей событий при рендеринге:
const tables = this.state.groups.map(group => {
const rows = group.words.map(({ _id, word, translations }) => (
<tr key = {_id}>
<td>{word}</td>
<td>{translations.join(', ')}</td>
<td><a href = "#" onClick = {event => {
this.deleteWord(event, group._id, _id)
}}>Delete</a></td>
</tr>
));
return <tbody key = {group._id}>{rows}</tbody>;
});
return <table>{tables}</table>;
Но я думаю, мне следует избегать этого, чтобы не создавать эти функции снова, если компонент когда-либо потребуется повторно отрисовать. Итак, другой вариант, о котором я думал, - это добавить атрибуты HTML5 data- * для хранения group_id и word_id, а затем получить к ним доступ через объект event. Я хотел бы знать, действительно ли это лучшая идея, и это мой второй вопрос.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете использовать мемоизацию при передаче объектов, массивов и функций компонентам, чтобы предотвратить их повторную визуализацию, если они не изменились. Несмотря на то, что таблица в целом изменилась, отдельные строки не изменились, поэтому нет причин повторно отображать каждую строку. Повторная визуализация таблицы не должна вызывать повторную визуализацию строк, если вы отделяете свои строки от компонента таблицы и / или предоставляете им соответствующие key.
Вы можете использовать key для ссылки на элементы в сопоставленном массиве, чтобы предотвратить их повторную визуализацию, когда в них ничего не изменилось. Когда таблица повторно отрисовывается, она увидит, что строка с ключом «value1» не изменилась, и, таким образом, не будет повторно отрисовывать эту строку. Он увидит, что строка с ключом "value2" отсутствует, и таким образом удалит ее из DOM. Он увидит, что строка с ключом «value3» не изменилась, и, следовательно, не будет повторно отображать эту строку.
Использование анонимной функции в вашем примере приводит к повторному созданию этой функции при каждом рендеринге. Это приводит к тому, что новый указатель передается как опора этому элементу, вызывая повторный рендеринг этого элемента. Вы должны запоминать свои встроенные функции, объекты и массивы.