0
votes

I have a component and I am trying to get data from graphql service (https://rickandmortyapi.com) using apollo, it comes okay, but I can't set the data using 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!)
    }
});

The error occurs when I am trying to set data using setCharacters method

TS2345: Argument of type 'Maybe<{ __typename?: "Character" | undefined; } & Pick>[]' is not assignable to parameter of type 'SetStateAction'.   Type 'Maybe<{ __typename?: "Character" | undefined; } & Pick>[]' is not assignable to type 'Character[]'.     Type 'Maybe<{ __typename?: "Character" | undefined; } & Pick>' is not assignable to type 'Character'.       Type 'null' is not assignable to type 'Character'.

My large graphql.tsx file with all types

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>;

I don't fully understand what is the problem with types and how to solve it ?

1

1 Answers

0
votes

In your type definitions you have got defined Characters response as:

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

This means that even if you use the ! command, forcing TS to believe that data.characters!.results! is an array and is not undefined, the Array elements still may be undefined (Array<Maybe<Character>>).

You can solve this in one of this way:

  1. Make sure your API always return a valid Character, and change return type to Array<Character>

  2. Change your useState definition to useState<Maybe<Character>[]>([]); and handle null characters in rendering (this seems very strange to me, but well)

  3. Filter null characters directly in onCompleted

However, I think you're getting wrong the query. The data variable you get from the useCharactersListQuery destructuring, will already contains the API response, you don't need to use another local React state to store it.