assuming I have a React app that has a internal state where I store an object like
const [car, setCar] = useState(new Car());
Assuming my class Car looks like the following:
class Car {
constructor(brand) {
this.carname = brand;
}
present() {
return "I have a " + this.carname;
}
}
When I run and debug the app, I can save a Car object into the state and also retrieve it and call present().
Now, when I make changes to the present() function, e.g.
present() {
return "I have a " + this.carname + " and it is shiny";
}
then due to Fast Refresh my App gets refreshed. But unfortunately, because the object is already stored in state, it will not receive the update of the function implementation.
Is there a way that I can change the code so that with a Fast Refresh the function implementation will be updated also for the object in React state?
I tried updating the method via prototype but it did also not work.
This is actually a bit tricky since what you are trying to achieve goes somewhat against react's design around immutable. Directly changing a classes method and assigning it to an existing instance can easily be done using Object.setPrototypeOf(car, Car.prototype)
.
This replaces the original car instance's prototype and changes the method implementation. As you've said this does not work because the mutating an object does not trigger a rerender.
useState compares objects using Object.is()
which won't trigger a rerender in this case since both objects have the same reference.
For the sake of Object.is()
, you need to pass in a new object with the new prototype and exact properties of the existing instance to setCar()
. This can't be done using the basic spread operator {..car}
or Object.assign({}, car)
since these methods only copy an object's own enumerable properties.
You can instead use the following to create a new object with the exact same properties the new prototype setCar(Object.assign(Object.create(Car.prototype), car))
This can be placed inside useEffect which will run everytime the original Car class is changed
const [car, setCar] = useState(new Car());
useEffect(() => {
setCar(Object.assign(Object.create(Car.prototype), car))
}, [])