0

我正在尝试使用 tsx 在 reactjs 中过滤具有多个键值的数据。Cards.tsx是父组件,ShipmentCard.tsx是子组件。我收到Property 'toLowerCase' does not exist on type 'never'错误。我只想根据搜索条件返回相关对象。谁能让我知道我在哪里犯了错误?

卡片.tsx

class Cards extends Component {
  state = {
    card: [],
    filter: ""
  };

  componentDidMount() {
    this.loadShipmentList();
  }

  handleChange = (event: any) => {
    this.setState({ filter: event.target.value });
  };

  loadShipmentList() {
    fetch("http://localhost:3001/shipments")
      .then(response => response.json())
      .then(data => this.setState({ card: data }));
  }

  render() {
    const { card, filter } = this.state;
    const lowercasedFilter = filter.toLowerCase();
    const filteredData = this.state.card.filter(item => {
      return Object.keys(item).some(key =>
        item[key].toLowerCase().includes(lowercasedFilter)
      );
    });

    return (
        <Card className="Search-Bar">
          <CardContent>
            <Grid container spacing={3}>
                <TextField
                  label="Search"
                  onChange={this.handleChange}
                />
            </Grid>
          </CardContent>
        </Card>
        <Grid container spacing={3}>
          {filteredData.map((card: any) => (
            <Grid item xs={12}>
              <ShipmentCard key={card.id} value={card} />
            </Grid>
          ))}
        </Grid>
    );
  }
}

export default Cards;
db.json

{
  "shipments": [
    {
      "id": "123",
      "name": "Heromisha",
      "total": "1000",
      "status": "ACTIVE",
      "userId": "E222"
    },
    {
      "id": "456",
      "name": "Honda",
      "total": "3000",
      "status": "ACTIVE",
      "userId": "E111"
    }
  ]
}

4

2 回答 2

2

Component是一个具有两个类型参数的通用接口:道具的类型和状态的类型。您的组件似乎没有道具,因此您可以仅使用objector{}用于道具接口,但是您确实有状态,因此您需要说明该状态具有什么形状

interface Card {
  id:     string;
  name:   string;
  total:  string;
  status: string;
  userId: string;
}
interface CardsState {
  card:   Card[];
  filter: string;
}
class Cards extends Component<object,CardsState> {
  state: CardsState = {
    card: [],
    filter: ""
  };
  // ...
}

另外,我怀疑你会遇到的接下来的几个问题是:

  1. 你没有检查fetch成功。你并不孤单,这是一个非常常见的错误(我把它写在我贫血的小博客上很常见)。您需要在fetch通话中添加支票:

    fetch("http://localhost:3001/shipments")
    .then(response => {
      if (!response.ok) {
        throw new Error("HTTP error " + response.status);
      }
      return response.json();
    })
    // ...
    
  2. 您没有处理来自fetch. loadShipmentList应该返回承诺链(然后componentDidMount和其他使用它的地方将处理错误)或自己处理/报告错误。

  3. 您正在设置card解析 JSON 的结果。您的代码假定card是一个数组,但 JSON 的顶层不是数组,它是一个具有shipments属性的对象(它是一个数组)。我怀疑您打算将card(应该是cards,复数或shipments)设置shipments为返回数据的属性:

    .then(({shipments}) => this.setState({ card: shipments }));
    //    ^^         ^^                          ^^^^^^^^^
    

可能还有其他问题,但上面的主要答案应该解决这个never问题,希望这些进一步的提示有所帮助。


在评论中,您曾说过,当您提供card类型时,此代码会导致错误:

const filteredData = this.state.card.filter(item => {
  return Object.keys(item).some(key =>
    item[key].toLowerCase().includes(lowercasedFilter)
  );
});

如果您希望 JSON 中数据的形状是可变的,您可以声明cardany[](或可能Record<string,string>[]),然后该动态代码将起作用。但前提是您希望组件由 JSON 驱动。否则,更新代码以使用类型安全的属性名称:

const filteredData = this.state.card.filter(({id, name, total, status, userId}) =>
  `${id}\t${name}\t${total}\t${status}\t${userId}`.toLowerCase().includes(lowercasedFilter)
);
于 2019-08-06T06:57:28.603 回答
2

编译器不是 state.card[...] 的真实类型,因为它被声明为数组。最简单的方法是将其声明为 any[]

  state = {
    card: [] as any[],
    filter: ""
  };

或显式描述数据类型

interface Card {
    "id": string,
    "name": string,
    "total": string,
    "status": string,
    "userId": string
}
...
      state = {
        card: [] as Card[],
        filter: ""
      };

或通过对象示例

const cardSample = {
      "id": "123",
      "name": "Heromisha",
      "total": "1000",
      "status": "ACTIVE",
      "userId": "E222"
  };

type Card = typeof cardSample;

    ...
          state = {
            card: [] as Card[],
            filter: ""
          };
于 2019-08-06T07:04:58.963 回答