允许typescript编译器仅在一个react​​状态属性上调用 setState

IT技术 reactjs typescript
2021-03-25 17:23:31

我在一个项目中使用 Typescript 和 React。Main 组件通过此接口获取传递状态。

interface MainState {
  todos: Todo[];
  hungry: Boolean;
  editorState: EditorState;  //this is from Facebook's draft js
}

但是,下面的代码(仅摘录)将无法编译。

class Main extends React.Component<MainProps, MainState> {
  constructor(props) {
    super(props);
    this.state = { todos: [], hungry: true, editorState: EditorState.createEmpty() };
  }
  onChange(editorState: EditorState) {
    this.setState({
      editorState: editorState
    });
  }
}

编译器抱怨说,在onChange我只尝试为一个属性设置状态的方法中,属性todos和属性hungry在 type 中丢失{ editorState: EditorState;}换句话说,我需要在onChange函数中设置所有三个属性的状态才能使代码编译。为了编译,我需要做

onChange(editorState: EditorState){
  this.setState({
    todos: [],
    hungry: false,
    editorState: editorState
  });
}

但此时没有理由在代码中设置todoshungry属性。在typescript/react中仅对一个属性调用 setState 的正确方法是什么?

6个回答

编辑

react 的定义已经更新,setState现在的签名是:

setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;

Pick<S, K>被内置其中在typescript2.1添加类型:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}

有关更多信息,请参阅映射类型
如果您仍然遇到此错误,那么您可能需要考虑更新您的react定义。

原答案:

我面临同样的事情。

我设法解决这个烦人的问题的两种方法是:

(1) 铸造/断言:

this.setState({
    editorState: editorState
} as MainState);

(2) 将接口字段声明为可选:

interface MainState {
    todos?: Todo[];
    hungry?: Boolean;
    editorState?: EditorState;
}

如果有人有更好的解决方案,我很乐意知道!


编辑

虽然这仍然是一个问题,但有两个关于将解决这个问题的新特性的讨论:
部分类型(现有类型的可选属性)

更准确的 Object.assign 类型和 React 组件 setState()

不,react 仅使用您在 中提供的属性更新状态setState,如果您尚未包含属性,则先前的值将保留
2021-05-28 17:23:31
实际上,我遇到了运行时错误bundle.js:20184 Uncaught TypeError: Cannot read property 'setState' of null不知道为什么this是空的。这显然与您的解决方案无关,但在我可以让它工作之前,我必须等待接受它作为正确答案。
2021-06-06 17:23:31
请注意,该断言as MainState仅在所有字段都不是可选的情况下才有效我已经对这个问题进行了相当多的探索,但还没有找到完整的解决方案TypeScript 需要支持“部分对象”才能正常工作。
2021-06-16 17:23:31
我已经给了你一个赞成票。它可以编译,但我无法完全尝试您的解决方案,因为我的应用程序还有其他问题(如果您觉得受到启发,这里有另一个typescript Q stackoverflow.com/questions/37302382/...)。关于您提供的这个答案,我唯一的担忧(我确定这可能不是真的,或者您会提到它)是您的转换解决方案会将其他状态属性设置为零,如果它们没有再次明确设置,或者演员只是忽略它并保持原样?
2021-06-19 17:23:31
好的,必须绑定onChange函数(出于某种原因),但让您的解决方案起作用。谢谢。
2021-06-20 17:23:31

显式更新状态,以计数器为例

this.setState((current) => ({ ...current, counter: current.counter + 1 }))
re-render all components是否会引用 的任何属性,state因为您正在传播...state
2021-06-01 17:23:31
当我这样做时,我收到错误 TypeScript error: Parameter 'state' implicitly has an 'any' type. TS7006
2021-06-01 17:23:31
使用 Lambda setState 将导致所有 setState 按照它们调用的顺序串行运行,因此只要您所依赖的 setState 在它们将被应用的依赖项之前运行(如语句 this.setState 中)为了使后者从先前的状态获得已经更新的状态,没有陈旧的状态。
2021-06-07 17:23:31
嗨,当我以这种方式调用 setstate 时,我的状态会异步更新。如果有多个方法正在运行,每个方法都在更新状态并且需要查看先前方法设置的状态,您将如何处理它?例如,在字段更新上,我可能会调用单独的方法来 1. 更新状态中的字段 2. 验证新值,更新状态中的一些错误消息, 3. 在状态中设置一个dirtyFlag。这些方法需要知道之前的方法对状态做了什么。最好的做法是在事件处理程序的最开始创建一个新的状态对象并传递它
2021-06-20 17:23:31

我认为最好的方法是使用 Partial

通过以下方式声明您的组件

class Main extends React.Component<MainProps, Partial<MainState>> {
}

Partial 会自动将所有键更改为可选。

如果您知道它们已定义怎么办?根据 TS 的说法,它们现在可以是未定义的。现在您需要通过以下方式明确告诉 TS 没关系property!
2021-06-04 17:23:31

我们可以setState通过参数化来判断它应该期待哪个字段<'editorState'>

this.setState<'editorState'>({
  editorState: editorState
});

如果您要更新多个字段,您可以像这样指定它们:

this.setState<'editorState' | 'hungry'>({
  editorState: editorState,
  hungry: true,
});

虽然它实际上会让你只指定其中一个!

无论如何,最新的 TS 和@types/react以上两个都是可选的,但它们引导我们......


如果您想使用动态 key,那么我们可以告诉setState您不要特别期待任何字段:

this.setState<never>({
  [key]: value,
});

如上所述,如果我们传递一个额外的字段,它不会抱怨。

GitHub 问题

编辑:请勿使用此解决方案,更喜欢https://stackoverflow.com/a/41828633/1420794 有关详细信息参阅评论。

现在扩展运算符已经在 TS 中发货,我的首选解决方案是

this.setState({...this.state, editorState}); // do not use !
目前仍然有一个缺点;TS 不会检测您是否添加了未声明的属性(即拼写错误)。如果我理解了 TS 团队的最后设计讨论,应该很快就会解决 =)
2021-05-25 17:23:31
你说得对=s!最好的解决方案是@Alexey-Petrushin's 我认为
2021-06-01 17:23:31
警告:请勿使用此解决方案!如果您这样做,那么您将很难检测到错误,因为 React 会批量更新状态。试想一下,这样的事情:state = { a: 1, b: 2, c: 3 }coolSetState({ a: 2, b: 3 })coolSetState({ c: 4 })其中,coolSetState = (editorState) => this.setState({...this.state, editorState}) 如果coolSetState({ c: 4 })因为以前的工作状态得到更新之前执行,你实际上可能最终与state ={ a: 1, b: 2, c: 4 } 我用速记符号。如果我有歧义,请告诉我。
2021-06-12 17:23:31
天哪,这是迄今为止最简单的方法。请对此答案点赞,以给予更多关注。
2021-06-19 17:23:31