I have a Fruit class:
export class Fruit {
constructor(public id: number, public name: string) {}
public changeName(_name: string): void {
console.log('changing name')
this.name = _name
}
}
And I implement it like so:
import React from 'react'
import { Fruit } from '../classes/fruit'
const HomePage = () => {
let fruit = new Fruit(1, 'apple')
return (
<div>
{fruit.name} <----- I am expecting this to update on the DOM when i click the button *********
<button onClick={() => fruit.changeName('banana')}>
change the name
</button>
</div>
)
}
export default HomePage
But when I click the button, the fruit name on the screen does not change. It stays as 'apple' . Does anyone know what I am doing wrong? I am new to Typescript
I have a Fruit class:
export class Fruit {
constructor(public id: number, public name: string) {}
public changeName(_name: string): void {
console.log('changing name')
this.name = _name
}
}
And I implement it like so:
import React from 'react'
import { Fruit } from '../classes/fruit'
const HomePage = () => {
let fruit = new Fruit(1, 'apple')
return (
<div>
{fruit.name} <----- I am expecting this to update on the DOM when i click the button *********
<button onClick={() => fruit.changeName('banana')}>
change the name
</button>
</div>
)
}
export default HomePage
But when I click the button, the fruit name on the screen does not change. It stays as 'apple' . Does anyone know what I am doing wrong? I am new to Typescript
Share Improve this question edited Dec 17, 2021 at 21:07 Aluan Haddad 31.9k10 gold badges83 silver badges95 bronze badges asked Dec 17, 2021 at 20:24 JarJar 2,0402 gold badges27 silver badges30 bronze badges 02 Answers
Reset to default 5Some notes:
React functional ponents do not work like that. If your data changes over time, you need to define some state, with a hook:
const [fruit, setFruit] = React.useState(initialFruit)
.That still won't work, as your
fruit
is a mutable object, React does not "see" in-place imperative updates. Not ideal, but you could fool React by using an object wrapper as the state value:{ value: someFruit }
(this works because in JS{ value: 1 } !== { value: 1 }
.Consider writing classes with immutable setters (
public changeName(name: string): Fruit
), and you'll be able to write nice declarative code like this:<button onClick={() => setFruit(fruit.changeName('banana'))}>
.
export class Fruit {
constructor(public id: number, public name: string) {}
public changeName(name: string): Fruit {
return new Fruit(this.id, name)
}
}
So, here's the thing. This isn't going to work.
First of all, any variables created in a react functional ponent's render function only exist during that render. So when you do:
let fruit = new Fruit(1, 'apple')
Then every single time your ponent renders you create a new Fruit
with an id
of 1
a name of "apple"
. Any changes you make to that object after the render will never be seen, because to be seen the ponent would need to re-rerender, which makes a new Fruit
from scratch.
The way you get around this is to use "state" which preserves values between ponent renders.
So instead, let's say you had this:
const HomePage = () => {
let [fruit, setFruit] = useState(new Fruit(1, 'apple'))
//...
}
But the problem is that state is expected to be immutable, which means if the state were to change it expects a new object entirely, and it's forbidden to change a piece of the state.
This is because react can't tell if anything actually change in state unless you replace the state entirely.
So to fix that you would need to set a brand new object when you change the state.
This should work:
const HomePage = () => {
let [fruit, setFruit] = useState(new Fruit(1, 'apple'))
return (
<div>
{fruit.name}
<button onClick={() => setFruit(new Fruit(fruit.id, 'banana')}>
change the name
</button>
</div>
)
}
But that's kid of annoying. This is why it's not remended to put mutable instances in state. And that means there usually isn't much need for instances of a class at all, and it's usually far simpler to just store objects in state.
// declare state
let [fruit, setFruit] = useState({ id: 1, name: 'apple' })
// set state
setFruit({ ...fruit, name: 'banana' })