如何使用打字稿中的反应钩子设置graphql数据?

Posted

技术标签:

【中文标题】如何使用打字稿中的反应钩子设置graphql数据?【英文标题】:How to set graphql data using react hooks in typescript? 【发布时间】:2020-08-09 22:06:24 【问题描述】:

我有一个组件,我正在尝试使用 apollo 从 graphql 服务 (https://rickandmortyapi.com) 获取数据,一切正常,但我无法使用 react hooks 设置数据:

const [characters, setCharacters] = useState<Character[]>([]);

const data, error, loading, refetch = useCharactersListQuery(
    variables: name: String(search),
    onCompleted: (data)=>
        //here I can see data very well
        console.log(data.characters!)
        //this line gives me an ERROR
        setCharacters(data.characters!.results!)
    
);

当我尝试使用 setCharacters 方法设置数据时发生错误

TS2345: 'Maybe[]' 不是 可分配给“SetStateAction”类型的参数。类型 '也许[]' 不能分配给类型'Character[]'。 键入'也许' 不可分配给类型 '特点'。类型“null”不能分配给类型“Character”。

我所有类型的大型 graphql.tsx 文件

import gql from 'graphql-tag';
import * as React from 'react';
import * as ApolloReactCommon from '@apollo/react-common';
import * as ApolloReactComponents from '@apollo/react-components';
import * as ApolloReactHoc from '@apollo/react-hoc';
import * as ApolloReactHooks from '@apollo/react-hooks';
export type Maybe<T> = T | null;
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = 
  ID: string;
  String: string;
  Boolean: boolean;
  Int: number;
  Float: number;
  /** The `Upload` scalar type represents a file upload. */
  Upload: any;
;

export type Query = 
   __typename?: 'Query';
  /** Get a specific character by ID */
  character?: Maybe<Character>;
  /** Get the list of all characters */
  characters?: Maybe<Characters>;
  /** Get a specific locations by ID */
  location?: Maybe<Location>;
  /** Get the list of all locations */
  locations?: Maybe<Locations>;
  /** Get a specific episode by ID */
  episode?: Maybe<Episode>;
  /** Get the list of all episodes */
  episodes?: Maybe<Episodes>;
;


export type QueryCharacterArgs = 
  id?: Maybe<Scalars['ID']>;
;


export type QueryCharactersArgs = 
  page?: Maybe<Scalars['Int']>;
  filter?: Maybe<FilterCharacter>;
;


export type QueryLocationArgs = 
  id?: Maybe<Scalars['ID']>;
;


export type QueryLocationsArgs = 
  page?: Maybe<Scalars['Int']>;
  filter?: Maybe<FilterLocation>;
;


export type QueryEpisodeArgs = 
  id?: Maybe<Scalars['ID']>;
;


export type QueryEpisodesArgs = 
  page?: Maybe<Scalars['Int']>;
  filter?: Maybe<FilterEpisode>;
;

export type Character = 
   __typename?: 'Character';
  /** The id of the character. */
  id?: Maybe<Scalars['ID']>;
  /** The name of the character. */
  name?: Maybe<Scalars['String']>;
  /** The status of the character ('Alive', 'Dead' or 'unknown'). */
  status?: Maybe<Scalars['String']>;
  /** The species of the character. */
  species?: Maybe<Scalars['String']>;
  /** The type or subspecies of the character. */
  type?: Maybe<Scalars['String']>;
  /** The gender of the character ('Female', 'Male', 'Genderless' or 'unknown'). */
  gender?: Maybe<Scalars['String']>;
  /** The character's origin location */
  origin?: Maybe<Location>;
  /** The character's last known location */
  location?: Maybe<Location>;
  /**
   * Link to the character's image.
   * All images are 300x300px and most are medium shots or portraits since they are intended to be used as avatars.
   */
  image?: Maybe<Scalars['String']>;
  /** Episodes in which this character appeared. */
  episode?: Maybe<Array<Maybe<Episode>>>;
  /** Time at which the character was created in the database. */
  created?: Maybe<Scalars['String']>;
;

export type Location = 
   __typename?: 'Location';
  /** The id of the location. */
  id?: Maybe<Scalars['ID']>;
  /** The name of the location. */
  name?: Maybe<Scalars['String']>;
  /** The type of the location. */
  type?: Maybe<Scalars['String']>;
  /** The dimension in which the location is located. */
  dimension?: Maybe<Scalars['String']>;
  /** List of characters who have been last seen in the location. */
  residents?: Maybe<Array<Maybe<Character>>>;
  /** Time at which the location was created in the database. */
  created?: Maybe<Scalars['String']>;
;

export type Episode = 
   __typename?: 'Episode';
  /** The id of the episode. */
  id?: Maybe<Scalars['ID']>;
  /** The name of the episode. */
  name?: Maybe<Scalars['String']>;
  /** The air date of the episode. */
  air_date?: Maybe<Scalars['String']>;
  /** The code of the episode. */
  episode?: Maybe<Scalars['String']>;
  /** List of characters who have been seen in the episode. */
  characters?: Maybe<Array<Maybe<Character>>>;
  /** Time at which the episode was created in the database. */
  created?: Maybe<Scalars['String']>;
;

export type FilterCharacter = 
  name?: Maybe<Scalars['String']>;
  status?: Maybe<Scalars['String']>;
  species?: Maybe<Scalars['String']>;
  type?: Maybe<Scalars['String']>;
  gender?: Maybe<Scalars['String']>;
;

export type Characters = 
   __typename?: 'Characters';
  info?: Maybe<Info>;
  results?: Maybe<Array<Maybe<Character>>>;
;

export type Info = 
   __typename?: 'Info';
  /** The length of the response. */
  count?: Maybe<Scalars['Int']>;
  /** The amount of pages. */
  pages?: Maybe<Scalars['Int']>;
  /** Number of the next page (if it exists) */
  next?: Maybe<Scalars['Int']>;
  /** Number of the previous page (if it exists) */
  prev?: Maybe<Scalars['Int']>;
;

export type FilterLocation = 
  name?: Maybe<Scalars['String']>;
  type?: Maybe<Scalars['String']>;
  dimension?: Maybe<Scalars['String']>;
;

export type Locations = 
   __typename?: 'Locations';
  info?: Maybe<Info>;
  results?: Maybe<Array<Maybe<Location>>>;
;

export type FilterEpisode = 
  name?: Maybe<Scalars['String']>;
  episode?: Maybe<Scalars['String']>;
;

export type Episodes = 
   __typename?: 'Episodes';
  info?: Maybe<Info>;
  results?: Maybe<Array<Maybe<Episode>>>;
;

export enum CacheControlScope 
  Public = 'PUBLIC',
  Private = 'PRIVATE'



export type CharactersListQueryVariables = 
  name?: Maybe<Scalars['String']>;
;


export type CharactersListQuery = (
   __typename?: 'Query' 
  &  characters?: Maybe<(
     __typename?: 'Characters' 
    &  info?: Maybe<(
       __typename?: 'Info' 
      & Pick<Info, 'count'>
    )>, results?: Maybe<Array<Maybe<(
       __typename?: 'Character' 
      & Pick<Character, 'id' | 'name' | 'image'>
    )>>> 
  )> 
);


export const CharactersListDocument = gql`
    query charactersList($name: String) 
  characters(page: 1, filter: name: $name) 
    info 
      count
    
    results 
      id
      name
      image
    
  

    `;
export type CharactersListComponentProps = Omit<ApolloReactComponents.QueryComponentOptions<CharactersListQuery, CharactersListQueryVariables>, 'query'>;

    export const CharactersListComponent = (props: CharactersListComponentProps) => (
      <ApolloReactComponents.Query<CharactersListQuery, CharactersListQueryVariables> query=CharactersListDocument ...props />
    );

export type CharactersListProps<TChildProps = , TDataName extends string = 'data'> = 
      [key in TDataName]: ApolloReactHoc.DataValue<CharactersListQuery, CharactersListQueryVariables>
     & TChildProps;
export function withCharactersList<TProps, TChildProps = , TDataName extends string = 'data'>(operationOptions?: ApolloReactHoc.OperationOption<
  TProps,
  CharactersListQuery,
  CharactersListQueryVariables,
  CharactersListProps<TChildProps, TDataName>>) 
    return ApolloReactHoc.withQuery<TProps, CharactersListQuery, CharactersListQueryVariables, CharactersListProps<TChildProps, TDataName>>(CharactersListDocument, 
      alias: 'charactersList',
      ...operationOptions
    );
;

/**
 * __useCharactersListQuery__
 *
 * To run a query within a React component, call `useCharactersListQuery` and pass it any options that fit your needs.
 * When your component renders, `useCharactersListQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const  data, loading, error  = useCharactersListQuery(
 *   variables: 
 *      name: // value for 'name'
 *   ,
 * );
 */
export function useCharactersListQuery(baseOptions?: ApolloReactHooks.QueryHookOptions<CharactersListQuery, CharactersListQueryVariables>) 
        return ApolloReactHooks.useQuery<CharactersListQuery, CharactersListQueryVariables>(CharactersListDocument, baseOptions);
      
export function useCharactersListLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions<CharactersListQuery, CharactersListQueryVariables>) 
          return ApolloReactHooks.useLazyQuery<CharactersListQuery, CharactersListQueryVariables>(CharactersListDocument, baseOptions);
        
export type CharactersListQueryHookResult = ReturnType<typeof useCharactersListQuery>;
export type CharactersListLazyQueryHookResult = ReturnType<typeof useCharactersListLazyQuery>;
export type CharactersListQueryResult = ApolloReactCommon.QueryResult<CharactersListQuery, CharactersListQueryVariables>;

我不完全理解类型有什么问题以及如何解决它?

【问题讨论】:

【参考方案1】:

在您的类型定义中,您已将 Characters 响应定义为:

export type Characters = 
   __typename?: 'Characters';
  info?: Maybe<Info>;
  results?: Maybe<Array<Maybe<Character>>>;
;

这意味着即使你使用!命令,强制TS相信data.characters!.results!是一个数组并且不是未定义的,数组elements仍然可能是未定义的(Array&lt;Maybe&lt;Character&gt;&gt; )。

您可以通过以下方式之一解决此问题:

    确保您的 API 始终返回有效字符,并将返回类型更改为 Array&lt;Character&gt;

    将您的 useState 定义更改为 useState&lt;Maybe&lt;Character&gt;[]&gt;([]); 并在渲染中处理 null 字符(这对我来说似乎很奇怪,但很好)

    直接在onCompleted中过滤null字符

但是,我认为您的查询弄错了。从useCharactersListQuery 解构得到的data 变量已经包含了API 响应,你不需要使用另一个本地React 状态来存储它。

【讨论】:

以上是关于如何使用打字稿中的反应钩子设置graphql数据?的主要内容,如果未能解决你的问题,请参考以下文章

反应导航:如何在打字稿中使用 NavigationStackScreenComponent 类型传递 redux 道具

如何将方法的泛型类型限制为打字稿中的对象?

在打字稿中返回反应 16 个数组元素

如何在打字稿中访问嵌套条件类型

我如何遍历表单数组并将其值设置为打字稿中的另一个数组?

如何在反应钩子中使用graphql钩子集成多个客户端