Реализация функции 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, чтобы предотвратить любые неопределенные ошибки.