Зачем использовать Object.assign () для обновления изменений свойств функциональных компонентов?

У меня есть список студентов, и я показываю их на столе. Есть две кнопки, которые указывают, по какому значению следует отсортировать список (имя или дата рождения). Когда нажимается кнопка и я сортирую список, сам список сортируется, но он не обновляется, если я не назначаю список новому списку с помощью Object.assign(newList, oldList), а затем обновляю его, переходя к функции состояния обновления updateList(newList). Вот мой код:

const students= [
  {
    name: "John Done",
    year: 1,
    birthdate: "2020-01-24",
  },
  {
    name: "Another Done",
    year: 3,
    birthdate: "2002-02-20",
  },
  {
    name: "Jack London",
    year: 2,
    birthdate: "1800-01-04",
  },
  {
    name: "Text Name",
    year: 3,
    birthdate: "1990-02-24",
  },
  {
    name: "Name",
    year: 2,
    birthdate: "2005-04-01",
  },
];

ReactDOM.render(<App students={students} />, document.getElementById('root'));

function App({ students }) {
  const [studentsList, setStudentsList] = useState(students);

  const sortByYear = () => {
    // let sortedstudents = [];
    // Object.assign(sortedStudents, studentsList);
    // sorteStudents.sort((a, b) => b.year - a.year);
    // console.log(sorteStudents);
    // setStudentsList(sortedStudents);
    studentsList.sort((a,b) => b.year - a.year));
    setStudentsList(studentsList);
  };

const sortByDates = () => {
  // let sortedStudents = [];
  // Object.assign(sortedStudents, studentsList);
  // sortedStudents.sort((a, b) => new Date(b.birthdate) - new Date(a.birthdate));
  // console.log(sortedStudents);
  // setStudentsList(sortedStudents);
  studentsList.sort((a, b) => new Date(b.birthdate) - new Date(a.birthdate));
  setStudentsList(studentsList);
};

return (
<div className="App">
  <div>
    <label>
      Sort By
    </label>
    <button
      onClick={() => sortByYear()}
    >
      Year
    </button>
    <button
      onClick={() => sortByDates()}
    >
      Most Old
    </button>
  </div>
  <Students students={studentsList} />
</div>
 );
}

Студенческий компонент

function Students({ students }) {
return (
    <div>
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Year</th>
            <th>Date of birth</th>
          </tr>
        </thead>
        <tbody>
          {students.map((student, index) => (
            <tr key={index}>
              <td>{student.name}</td>
              <td>{student.year.toString()}</td>
              <td>{student.birthdate}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Таким образом, хотя список студентов сортируется, состояние не обновляется, но если я назначу исходный список новому, а затем отсортирую его, а затем обновлю состояние, в котором он работает.

РАБОТАЕТ

let sortedStudents = [];
Object.assign(sortedStudents, studentsList);
sortedStudents.sort((a, b) => new Date(b.birthdate) - new Date(a.birthdate));
//console.log(sortedStudents);
setStudentsList(sortedStudents)

НЕ РАБОТАЕТ

studentsList.sort((a, b) => new Date(b.birthdate) - new Date(a.birthdate));
setStudentsList(studentsList);

Итак, вопрос в том, почему мне нужно назначать свой studentsList новому массиву, в частности, используя Object.assign(), чтобы setStudentsList() обновлял состояние компонента? Я только начал изучать React, поэтому меня действительно сбивает с толку то, как на самом деле работают эти состояния.

Похожие сообщения, которые я нашел


person Miraziz    schedule 30.12.2020    source источник
comment
React определяет, следует ли выполнять повторный рендеринг или нет, основываясь на равенстве предыдущих реквизитов следующему реквизиту и предыдущего состояния следующему состоянию. Изменив исходный массив состояний, предыдущий studentList имеет ссылочное равенство с обновленным studentList, и React не обнаружит, что ему нужно повторно отрендерить.   -  person Brian Thompson    schedule 30.12.2020
comment
sort изменяет состояние, а assign - нет.   -  person Dennis Vash    schedule 30.12.2020
comment
@BrianThompson Теперь это имеет смысл ... Ваш комментарий отвечает на мой вопрос. Пожалуйста, дайте мне знать, могу ли я улучшить свой код, потому что я еще не знаком с лучшими практиками.   -  person Miraziz    schedule 30.12.2020
comment
Вы не хотите Object.assign здесь. Вам нужен const sortedStudents = studentsList.slice(); (или любая альтернатива, копирующая массив).   -  person Bergi    schedule 30.12.2020
comment
@Miraziz lol не стал бы называть это лучшими практиками, как ограничениями дизайна. Я уверен, что команда React хотела бы, чтобы более короткая версия просто работала, если бы они могли делать это надежно и с хорошей производительностью: P   -  person Jared Smith    schedule 31.12.2020
comment
@JaredSmith ха-ха, да, я только изучаю React, и мне бы хотелось, если я правильно понимаю и изучаю концепции.   -  person Miraziz    schedule 31.12.2020


Ответы (1)


Как упоминалось в Brain: React определяет, следует ли выполнять повторный рендеринг или нет, основываясь на равенстве предыдущих реквизитов следующему. props и предыдущее состояние в следующее состояние. Изменяя исходный массив состояний, предыдущий studentList имеет ссылочное равенство с обновленным studentList, и реагирование не обнаружит, что ему нужно повторно отрендерить.

Таким образом, чтобы изменить список и реагировать на обнаружение изменений, необходимо использовать (изменить) значение массива вместо его ссылки.

Как намекнул Берги: альтернатива, копирующая массив по значению

person Miraziz    schedule 31.12.2020