Реализация функции Fast Refresh + Update объекта, находящегося в состоянии React

Предполагая , что у меня есть приложение React, которое имеет внутреннее состояние, в котором я храню такой объект, как

  const [car, setCar] = useState(new Car());

Предполагая, что мой класс Car выглядит следующим образом:

class Car {
  constructor(brand) {
    this.carname = brand;
  }

  present() {
    return "I have a " + this.carname;
  }
}

Когда я запускаю и отлаживаю приложение, я могу сохранить объект Car в состоянии, а также получить его и вызвать present().

Теперь, когда я вношу изменения в функцию present (), например

  present() {
    return "I have a " + this.carname + " and it is shiny";
  }

затем из-за быстрого обновления мое приложение обновляется. Но, к сожалению, поскольку объект уже хранится в состоянии, он не получит обновление реализации функции.

Есть ли способ изменить код, чтобы при быстром обновлении реализация функции также обновлялась для объекта в состоянии React?

Я пробовал обновить метод через прототип, но он тоже не сработал.

2 ответа

Решение

На самом деле это немного сложно, так как то, что вы пытаетесь достичь, несколько противоречит дизайну реакции вокруг неизменяемого. Непосредственное изменение метода классов и присвоение его существующему экземпляру можно легко выполнить с помощью Object.setPrototypeOf(car, Car.prototype).

Это заменяет прототип оригинального экземпляра автомобиля и меняет реализацию метода. Как вы сказали, это не работает, потому что изменение объекта не вызывает повторную визуализацию.

useState сравнивает объекты, используя Object.is() который в этом случае не вызовет повторную визуализацию, поскольку оба объекта имеют одинаковую ссылку.

Во имя Object.is(), вам необходимо передать новый объект с новым прототипом и точными свойствами существующего экземпляра в setCar(). Этого нельзя сделать с помощью основного оператора спреда. {..car} или же Object.assign({}, car) поскольку эти методы копируют только собственные перечислимые свойства объекта.

Вместо этого вы можете использовать следующее, чтобы создать новый объект с теми же свойствами, что и новый прототип. setCar(Object.assign(Object.create(Car.prototype), car))

Его можно поместить в useEffect, который будет запускаться каждый раз при изменении исходного класса Car.

 const [car, setCar] = useState(new Car());

 useEffect(() => {
    setCar(Object.assign(Object.create(Car.prototype), car))
  }, [])

По задумке Fast Refresh сохраняет состояние всякий раз, когда это возможно. Однако useEffect всегда будет обновляться во время быстрого обновления, независимо от того, что находится в массиве зависимостей. Пока вы предоставляете пустой массив зависимостей, вы можете использовать setState внутри useEffect для изменения состояния между быстрыми обновлениями.

 const [car, setCar] = useState(new Car());

 useEffect(() => {
    setCar(new Car());
  },[])

Однако имейте в виду, что useEffect будет запускаться только после первого рендеринга, поэтому вам нужно сохранить состояние по умолчанию new Car() в аргументе ловушки useState, чтобы предотвратить любые неопределенные ошибки.

Другие вопросы по тегам