【问题标题】:How to correctly set initial state in React with Typescript without constructor?如何在没有构造函数的情况下使用 Typescript 在 React 中正确设置初始状态?
【发布时间】:2019-03-15 20:33:48
【问题描述】:

在现代 JS 中,我们可以像这样直接为 React 组件设置初始状态:

class App extends React.Component {
  state = {value: 10}

  render() {
    return <div>{this.state.value}</div>
  }
}

当我尝试使用 Typescript 执行此操作时,TSLint 说“类属性 'state' 必须标记为 'private'、'public' 或 'protected'”。如果我将其设置为“私有”,linter 将在App 上报告Class 'App' incorrectly extends base class 'Component&lt;{}, { value: number; }, any&gt;'. Property 'state' is private in type 'App' but not in type 'Component&lt;{}, { value: number; }, any&gt;'.。我知道我可以调整 linter 规则来跳过这种检查,但是检查类属性的可见性通常是我想要利用的一件好事。

测试完所有三个选项后,仅选择“public”不会让 TSLint 抛出错误。但是由于这里的 state 代表了这个特定组件的内部状态,将其设置为 public 似乎很奇怪。我的做法是否正确?

class App extends React.Component<{}, { value: number }> {
  public state = { value: 10 };

  public render() {
    return <div>{this.state.value}</div>;
  }
}

在我在网上找到的所有 TS-React 教程中,都使用了构造函数,就像在旧的 JS 语法中一样。

class App extends React.Component<{}, { value: number }> {
  constructor(props: any) {
    super(props);
    this.state = { value: 10 };
  }

  public render() {
    return <div>{this.state.value}</div>;
  }
}

在 Typescript 中直接设置类属性是否被认为是一种不好的做法/样式?

【问题讨论】:

  • 我通常避免使用构造函数,因为设置属性不需要它(现代 JavaScript 也不再需要它)。我不认为这是不好的做法/不好的风格。如果您不指定关键字public,它将自动公开。如果你愿意,你可以使用private,因为无论如何你都不会在课堂外使用状态,但最终它并不重要。
  • 是的,我在写JS的时候也避免使用构造函数。在这种情况下,如果我使用private,TSlint 将在App 上报告Class 'App' incorrectly extends base class 'Component&lt;{}, { value: number; }, any&gt;'. Property 'state' is private in type 'App' but not in type 'Component&lt;{}, { value: number; }, any&gt;'.。我知道我可以将 TSlint 配置为隐藏这种检查,但是检查类属性的可见性通常是我想要利用的一件好事。
  • stackoverflow.com/a/52688791/3731501在我在网上找到的所有教程中,都使用了构造函数,就像在旧的 JS 语法中一样 - 编写教程的人不一定精通他们所写的东西。 TSlint 说“类属性 'state' 必须标记为 'private'、'public' 或 'protected'”。如果我使用私有,TSlint 会报告 - 禁用此规则,public 是多余的。 state 不应该是私人的。
  • “将其设置为公开似乎很奇怪”。 Component 中的 public 就是这样。这没什么好担心的。
  • @cartant OP 声明第一个 sn-p 产生 linter 错误。这表明存在明确指定可见性的 lint 规则(我从未注意到这样的规则,因为它似乎没用)。

标签: reactjs typescript class constructor


【解决方案1】:

我的做法是否正确?

是的。

在 Typescript 中直接设置类属性是否被认为是不好的样式?

没有。

稍微好一点的方法

考虑将状态声明为public readonly,并使用Readonly modifier

这将满足 TSLint,同时也为您提供一些防止被错误修改的保护(即不使用 this.setState)。 即使状态仍然暴露在外部,这通常不是问题。

interface IState {
  value: number;
}

class App extends React.Component<{}, IState> {
  public readonly state: Readonly<IState> = {
    value: 10
  }

  public render() {
    return <div>{this.state.value}</div>
  }
}

TSLint 规则

显式声明访问修饰符是一件好事,即使它隐式地导致相同的访问。它有助于使您的代码保持清晰,因此我不会禁用此 TSLint 规则。

【讨论】:

  • 默认情况下,状态在反应类型中是只读的。无需在顶部应用您自己的只读。
  • "无需在顶部应用您自己的只读。" - 不正确。如果不将状态声明为readonly,编译器将允许完全替换状态的代码,例如。 this.state = { value: 10 };。而且,如果不使用Readonly 修饰符,编译器将允许不正确地更新状态成员的代码,例如。 this.state.value = 10;。自己尝试一下,你会发现如果没有这些修饰符,编译器将无法保护你免于做错事。
  • readonly state = {} 没问题。尽管 Readonly 是多余的。见:github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/…
【解决方案2】:

我想说直接设置 React 组件的 state 属性是不好的做法除非您将类型注释为与React.Component 的状态类型参数相同。如果没有注释,TypeScript 从初始化程序确定 this.state 的类型,覆盖超类中的类型(基于 state 类型参数),并且如果初始化程序的类型是类型参数的子类型。这可能会导致以后出现不健全或意外行为;示例见this thread

【讨论】:

  • 是的,我同意。我查看了 React.Component 的类型定义,状态似乎没有注释,所以 public 这里应该是正确使用的关键字:)
【解决方案3】:

状态是不可变的。设置初始状态的推荐方法是由构造函数。

请注意,“状态”已经定义并继承,因此无需重新声明:

interface AppState {
    charge : number;
    spin   : number;
    color  : string;
}

interface AppProps {}

class App extends React.Component<AppProps, AppState> {

    constructor(props) {
        super(props);
        this.state={ charge: 0, spin: -0.5, color:'red' }; 
    }
}

顺便说一句,这正是react documentations 中定义的方式:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多