Home > Enterprise >  How do I change a value in an instance of a Typescript class?
How do I change a value in an instance of a Typescript class?

Time:12-18

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

CodePudding user response:

Some notes:

  • React functional components do not work like that. If your data changes over time, you need to use 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 }.

  • As you see, React's whole idea involves immutable values (AKA functional programming). 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)
    }
}

CodePudding user response:

So, here's the thing. This isn't going to work.


First of all, any variables created in a react functional component's render function only exist during that render. So when you do:

let fruit = new Fruit(1, 'apple')

Then every single time your component 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 component 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 component 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 recommended 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' })
  • Related