сортировка массива объектов и обновление состояния

#javascript #arrays #reactjs #object

Вопрос:

ОБНОВЛЕНИЕ: Вот ссылка на codesandbox: https://codesandbox.io/s/15tn7?file=/client/src/App.js Однако есть проблема в том, что приложение не отображается в браузере codesandbox. Может кто-нибудь помочь мне понять, в чем проблема с этим? Я не очень хорошо знаком с codesandbox.

У меня есть компонент, который в основном представляет собой таблицу списка сотрудников. Таблица сотрудники составляется из массива объектов, называемых сотрудниками, в котором каждый объект имеет ключ фамилии (среди прочих). Я хочу иметь возможность нажать, чтобы перейти на вкладку «Имя» таблицы, чтобы расположить сотрудников в алфавитном порядке по фамилии. Я создал функцию для этого:

 const nameSort = () => {
  let employeeCopy = [];
  employeeCopy = employees;
  employeeCopy.sort((a, b) => {
    if (a.last_name.toLowerCase() < b.last_name.toLowerCase()) return -1;
    if (a.last_name.toLowerCase() > b.last_name.toLowerCase()) return 1;
    return 0;
  });
  setEmployees(employeeCopy);
  console.log("name sort ran");
  console.log("employees: ", employees);
};
 

Функция работает просто отлично. Когда я утешаю.войдите в массив сотрудников, он выходит точно так, как я хочу, в алфавитном порядке по ключу фамилии. Однако компонент никогда не обновляется на основе этого вновь отсортированного массива. Может ли кто-нибудь помочь мне понять, как исправить эту проблему?

Вот как отображается таблица:

 <table className="et-tbl">
  <tr>
    <th onClick={nameSort}>Name</th>
    <th onClick={emailSort}>Email</th>
    <th>Phone</th>
    <th></th>
  </tr>
  {employees.map((employee) => (
    <tr key={employee.id}>
      <td>
        <div
          className="et-tbl-name-cell"
          onClick={() => renderProfile(employee, employee.id)}
        >
          {employee.photo ? (
            <img
              src={employee.photo}
              alt={`${employee.first_name} ${employee.last_name}`}
            />
          ) : (
            <div></div>
          )}
          {employee.last_name}, {employee.first_name}
        </div>
      </td>
      <td>{employee.email}</td>
      <td>{employee.phone}</td>
      <td className="et-tbl-icon-cell">
        <div
          className="et-tbl-icon"
          onClick={() => renderProfile(employee, employee.id)}
        >
          <AiFillProfile />
        </div>
        <div
          className="et-tbl-icon"
          onClick={() => editEmployee(employee)}
        >
          <MdModeEdit />
        </div>
        <div className="et-tbl-icon">
          <VscChromeClose />
        </div>
      </td>
    </tr>
  ))}
</table>
 

введите описание изображения здесь

введите описание изображения здесь

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

1. пожалуйста, не могли бы вы приложить полный код ? codesandbox может быть хорошим вариантом

2. Покажите нам полный код.

3. Попробуй setEmployees([...employeeCopy])

4. @Codenewbie Вот ссылка на codesandbox: codesandbox.io/s/15tn7?file=/client/src/App.js

Ответ №1:

Я не смог протестировать свой код (так что я мог ошибаться), но эта строка в вашем коде:

 employeeCopy = employees;
 

не создает копию сотрудников, поскольку сотрудники-это сложный тип (т. е. массив).

Кроме того, Array.prototype.sort сортирует массив на месте (вместо создания копии).

Эти два пункта выше являются проблемой в данном контексте, поскольку это означает, что ссылка на employees никогда не меняется (поскольку массив мутирует на месте). Способ, которым React обнаруживает изменения состояния в сложных типах (объектах, массивах и т. Д.), Заключается в поверхностной проверке того, изменилась ли их ссылка.


Непроверено, но я думаю, что функцию nameSort можно переписать что-то вроде:

 const nameSort = () => {
  setEmployees((prev) => {
    return [...prev].sort((a, b) => {
      if (a.last_name.toLowerCase() < b.last_name.toLowerCase()) return -1;
      if (a.last_name.toLowerCase() > b.last_name.toLowerCase()) return 1;
      return 0;
    });
  });
};
 

Основной вывод состоит в том , чтобы использовать синтаксис распространения ... для создания неглубокой копии (хотя копия также могла быть получена с помощью методов массива: slice или map , например).


Функция emailSort может быть переписана аналогичным образом:

 const emailSort = () => {
  setEmployees((prev) => {
    return [...prev].sort((a, b) => {
      if (a.email.toLowerCase() < b.email.toLowerCase()) return -1;
      if (a.email.toLowerCase() > b.email.toLowerCase()) return 1;
      return 0;
    });
  });
};
 

Примечание: если это настоящие ключи API/учетные данные в коде, которым вы поделились, вы можете войти в службы (Firebase и т. Д.), отозвать эти учетные данные и создать новые, другие. Просто для того, чтобы никакие посторонние люди не могли (неправильно)использовать эти услуги, выдавая себя за вас.