【问题标题】:Map function and React setStateMap 函数和 React setState
【发布时间】:2020-09-03 20:36:17
【问题描述】:

我是 React 新手。

我需要在一个状态下更新对象的属性。 我尝试使用下面的代码并遇到错误。

你能解释一下我做错了什么吗?

export interface IResultSourcesListItemsState {
  listitems: [
{
  "Title": string,
  "sourceGuid": string,
  "isChecked": boolean
}
  ]
}

_

public constructor(props: IResultSourcesProps, state: IResultSourcesListItemsState) {
    super(props);
    this.state = {
      listitems: [
        {
          "Title": "",
          "sourceGuid": "",
          "isChecked": true
         }
       ],
     };
     ResultSources.siteUrl = this.props.webSiteUrl;
}

resultSourceValueChange(checked, value) {
    let stateChanged = false;
    const updatedState = this.state.listitems.map((listitem) => {
      if (listitem.Title === "All" && listitem.isChecked) {
        listitem.isChecked = false;
        stateChanged = true;
      }
      return listitem;
    });
    if (stateChanged)
      this.setState({listitems: updatedState});
}

我简化了我的代码, 现在这段代码可以工作了

const updatedState = this.state.listitems;
this.setState({listitems: updatedState});

但是这段代码没有编译,为什么?

const updatedState = [...this.state.listitems];
this.setState({listitems: updatedState});

【问题讨论】:

  • 为什么需要stateChanged 变量?
  • 仅在发生更改时更新状态
  • 能否添加完整代码。通常你可以使用componentDidUpdate 来做到这一点。这个的 Codesandbox 很有帮助。只需在此处添加最低限度的代码。如果我们有实时代码,它很容易调试
  • 这是一个有很多依赖项的大项目。把它复制到操场上并不容易:(
  • 能否包含定义 IResultSourcesListItemsState 的代码?

标签: reactjs typescript


【解决方案1】:

所以当我询问IResultSourcesListItemsState 的定义时,我以为你遇到了错误。您的错误与 TypeScript 相关,而不是 React。

当您使用语法 let foo:[string] = ... 时,foo 是具有单个元素的元组(始终具有单个元素的列表)。例如,let bar:[string, string, string] = ... 将始终具有三个元素。如果你想定义一个“普通”列表,你必须使用语法let baz:string[] = ...

由于map 的返回值是一个列表,而不是一个元组,并且你的状态下listitems 的类型是一个元组,你会得到一个错误。 TypeScript 不能保证 map 的返回总是只有一个元素,因此该类型与您的 listitems 属性之一不兼容。

要解决此问题,您需要重新定义您的接口IResultSourcesListItemsState,如下所示:

export interface IResultSourcesListItemsState {
  listitems: {
     "Title": string,
     "sourceGuid": string,
     "isChecked": boolean
  }[]
}

附带说明一下,在这里您直接在 map 函数上改变您的状态,这是一种不好的做法:

resultSourceValueChange(checked, value) {
    let stateChanged = false;
    const updatedState = this.state.listitems.map((listitem) => {
      if (listitem.Title === "All" && listitem.isChecked) {
        listitem.isChecked = false;
        stateChanged = true;
      }
      return listitem;
    });
    if (stateChanged)
      this.setState({listitems: updatedState});
}

【讨论】:

  • 谢谢@frangaren!你的答案是有效的。但我以 MS 的接口为例 - social.technet.microsoft.com/wiki/contents/articles/…。尝试找到 IReactGetItemsState
  • 你为什么说我变异了状态?据我所知,map() 方法创建一个新数组,其结果是为每个数组元素调用一个函数。此方法不会更改原始数组。我说的对吗?
  • 您对map 的看法是正确的,但listitem.isChecked = false; 确实改变了列表中的项目。
  • 谢谢!如何避免?我应该如何重新设计代码以避免变异?
  • 您可以使用return {...listitem, isChecked: false}; 而不是listitem.isChecked = false;,它创建列表项的副本,但isChecked 属性为false。您可以添加多个属性。请记住,这些属性是按顺序应用的,因此return {isChecked: false, ...listitem};' would keep the isChecked 的值为listitem。另外,请阅读this,因为如果您根据之前的状态改变状态,您可能会遇到问题。
【解决方案2】:

我终于解决了我的问题。

我只需要添加as any。所以这条线是

this.setState({listitems: updatedState} as any);

this.setState({listitems: updatedState} as Pick<IResultSourcesListItemsState, keyof IResultSourcesListItemsState>);

我不知道为什么会这样。 This topic 对我很有帮助。

【讨论】:

  • 检查我的答案,你可以解决你的错误,但你有一些误解,你的代码仍然没有正确定义。
猜你喜欢
  • 2018-11-13
  • 2020-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-08
  • 1970-01-01
  • 2016-09-02
  • 1970-01-01
相关资源
最近更新 更多