【问题标题】:How do I change a value in an instance of a Typescript class?如何更改 Typescript 类实例中的值?
【发布时间】:2022-01-20 16:41:41
【问题描述】:

我有一个水果课:

export class Fruit {
    constructor(public id: number, public name: string) {}

    public changeName(_name: string): void {
        console.log('changing name')
        this.name = _name
    }
}

我是这样实现的:

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

但是当我点击按钮时,屏幕上的水果名称并没有改变。它保持为“苹果”。有谁知道我做错了什么?我是打字稿的新手

【问题讨论】:

    标签: javascript reactjs typescript es6-class react-typescript


    【解决方案1】:

    一些注意事项:

    • React 函数式组件不是这样工作的。如果你的数据随时间变化,你需要定义一些状态,用一个钩子:const [fruit, setFruit] = React.useState(initialFruit)

    • 这仍然行不通,因为您的 fruit 是一个可变对象,React 不会“看到”就地命令式更新。不理想,但你可以通过使用对象包装器作为状态值来欺骗 React:{ value: someFruit }(这是因为在 JS 中 { value: 1 } !== { value: 1 }

    • 如您所见,React 的整个想法涉及不可变值(AKA 函数式编程)。考虑使用不可变的 setter (public changeName(name: string): Fruit) 编写类,您将能够编写像这样的漂亮声明性代码:&lt;button onClick={() =&gt; setFruit(fruit.changeName('banana'))}&gt;

    export class Fruit {
        constructor(public id: number, public name: string) {}
    
        public changeName(name: string): Fruit {
            return new Fruit(this.id, name)
        }
    }
    

    【讨论】:

    • 我 99% 都在跟踪你,但我似乎无法编译它。我不确定您在哪里创建用于设置 useState 等的新类实例,或者即使您正在创建一个类实例以设置为 useState。无论如何,您可以为您的示例提供允许声明式 changeName 调用的代码?
    • 不确定我是否遵循,但这不是一种 hacky 方法,即使不使用 React,您也不会触及现有对象,而是创建新对象。在不可变类上,“setter”只返回新实例。这就是函数式方法,它通常会产生更具声明性、更清晰的代码,因为您可以遵循工作流程,而不会在整个代码库中发生就地更新。在 google 中搜索“javascript 函数式编程不可变”。
    • > 您是否认为在类中也有相关方法的价值。是的,一个对象应该提供生命周期/更新方法;否则,使用它的代码会更加冗长,并且通常会破坏封装。
    • 确实,这些是不同的东西。您必须创建新值以便 React 重新渲染,这就是重点。它的完成方式(使用虚拟 DOM 或其他)是一个实现细节。
    • 没错:在 React 中,副作用只能在 useEffect 块内执行。您可以在类中添加一个有效的save 方法,或者您可以创建一个someFruitRepository.save(fruit) 来隔离持久性的模型/实体(这是一个实现细节)。在某些时候,您可以深入研究 hex/clean 架构。
    【解决方案2】:

    所以,事情就是这样。这是行不通的。


    首先,在 react 功能组件的渲染函数中创建的任何变量仅在该渲染期间存在。所以当你这样做时:

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

    然后,每次您的组件渲染时,您都会创建一个新的Fruit,其名称为1id,名称为"apple"。您在渲染后对该对象所做的任何更改都不会被看到,因为要看到组件需要重新渲染,这会从头开始创建一个新的Fruit


    解决这个问题的方法是使用“状态”,它在组件渲染之间保留值。

    所以,假设你有这个:

    const HomePage = () => {
        let [fruit, setFruit] = useState(new Fruit(1, 'apple'))
        //...
    }
    

    但问题是状态应该是不可变的,这意味着如果状态要改变,它需要一个完全新的对象,并且禁止改变状态的一部分。 p>

    这是因为除非你完全替换状态,否则 react 无法判断状态是否真正发生了变化。

    所以要解决这个问题,您需要在更改状态时设置一个全新的对象。

    这应该可行:

    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>
        )
    }
    

    但那是烦人的孩子。这就是为什么不建议将可变实例置于状态中的原因。这意味着通常根本不需要类的实例,而且将对象存储在状态中通常要简单得多。

    // declare state
    let [fruit, setFruit] = useState({ id: 1, name: 'apple' })
    
    // set state
    setFruit({ ...fruit, name: 'banana' })
    

    【讨论】:

      猜你喜欢
      • 2022-10-15
      • 1970-01-01
      • 2019-06-18
      • 2021-12-17
      • 1970-01-01
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多