在TypeScript React中对象可能为null

时间:2019-06-28 05:37:05

标签: reactjs typescript

使用以下代码:

const Products = () => {
  const classes = useStyles();

  const { data } = useFetch('/categories');

  return (
    <div className={classes.root}>
      <GridList cellHeight={180} className={classes.gridList}>
        <GridListTile key="Subheader" cols={6} style={{ height: 'auto' }} />
        {data &&
          data.map((category: CategoryInterface) => {
            return (
              <GridListTile key={category.id}>
                <GridListTileBar title={category.name} />
              </GridListTile>
            );
          })}
      </GridList>
    </div>
  );
};

在映射之前,我在数据上得到“对象可能为空”,我尝试使用&&运算符来摆脱它,并且还尝试定义一个变量const categories = data as CategoryInterface[],但这表明我必须转换为首先未知,我应该怎么做呢?

这是useFetch钩子

import { useEffect, useState } from 'react';

export const useFetch = (url: string) => {
  const [state, setState] = useState({ data: null, loading: true });

  useEffect(() => {
    setState(state => ({ data: state.data, loading: true }));

    fetch(url)
      .then(res => res.json())
      .then(json => setState({ data: json, loading: false }));
  }, [url, setState]);

  return state;
};

2 个答案:

答案 0 :(得分:0)

您的useFetch Hokk应该是这样的,

import { useEffect, useState } from 'react';

export const useFetch = (url: string) => {
  const [state, setState] = useState({ data: {}, loading: true }); //Don't assign null to state variable

  useEffect(() => {
    setState(state => ({ data: state.data, loading: true })); //Remove this line, this is not needed 

    fetch(url)
      .then(res => res.json())
      .then(json => setState({ data: json, loading: false }));
  }, [url, setState]); //Replace [url, setState] with [] as your second argument to make useEffect work for first time.

  return state;
};

您的用法应该是

const { data } = useFetch('/categories');
{data.length>0 &&
          data.map((category: CategoryInterface) => {
            return (
              <GridListTile key={category.id}>
                <GridListTileBar title={category.name} />
              </GridListTile>
            );
          })
}

答案 1 :(得分:0)

Typescript将类型分配给JavaScript变量。通常,在使用TS时,应在使用前定义变量类型。但是,TS有时可能会推断出变量的类型。

似乎您将JS代码复制粘贴到TypeScript中并试图使其正常工作。因此,我首先建议为要使用的变量定义类型,以便TS可以设置正确的类型。

useState的初始调用使state.data的类型为null(这是唯一的TS知道的类型)。与JS不同,TS不允许在执行期间更改类型。因此,state.data在程序执行期间将具有null类型。

const [state, setState] = useState({ data: null, loading: true });
// Type of state is
// { data: null, loading: boolean }

要纠正此问题,您应该提前提供state变量的类型。其中一个可能的值为null。另一个可能的值-是从fetch接收的数据。您可能应该知道fetch返回的数据的JSON类型是什么,因此请按类型输入。

从我的代码中我可以猜到,该数据类型可能看起来像这样

type IData = CategoryInterface[];

CategoryInterface看起来像

interface CategoryInterface {
    id: string;
    name: string; 
}

因此IData将是state.data的第二种可能类型。因此,在state通话期间分配useState的类型

const [state, setState] = useState<{data: IData | null, loading: boolean}>({ data: null, loading: true });

但是您应该保留{data && data.map (/*...*/)}的位置,因为state.data在数据完全加载之前是不确定的。

完整代码

interface CategoryInterface {
    id: string;
    name: string;
}

type IData = CategoryInterface[];

export const useFetch = (url: string) => {
    const [state, setState] = useState<{ data: IData | null, loading: boolean }>({ data: null, loading: true });

    useEffect(() => {
        setState(state => ({ data: state.data, loading: true }));

        fetch(url)
            .then(res => res.json())
            .then(json => setState({ data: json, loading: false }));
    }, [url, setState]);

    return state;
};