1

I was following a tutorial and was using the exact same code but because of a different dependency version, I get an error.

The Tutorial dependency version:

"jotai": "^1.9.0",
"jotai-tanstack-query": "^0.4.0",

My dependency version:

"jotai": "^2.1.0",
"jotai-tanstack-query": "^0.7.0",

The code:

import { atom } from "jotai";
import { atomsWithQuery } from "jotai-tanstack-query";

export interface Pokemon {
  id: number;
  name: string;
  type: string[];
  hp: number;
  attack: number;
  defense: number;
  special_attack: number;
  special_defense: number;
  speed: number;
}

export const searchAtom = atom("");

const [allPokemon] = atomsWithQuery<Pokemon[]>(() => ({
  queryKey: ["pokemon"],
  queryFn: async () => {
    const res = await fetch("/pokemon.json");
    return res.json();
  }
}));

export const pokemonAtom = atom((get) => {
  const search = get(searchAtom).toLowerCase();
  const all = get(allPokemon);
  return all.filter((p) => p.name.toLowerCase().includes(search)); // Error
});


I get error: Property 'filter' does not exist on type 'Pokemon[] | Promise<Pokemon[]>'. Property 'filter' does not exist on type 'Promise<Pokemon[]>'.ts(2339)

It appears atomsWithQuery will also return a promise in the newer version. (Type of allPokemon in old version: Pokemon[], new version Pokemon[] | Promise<Pokemon[]> ) How can I avoid/fix this error? Thank you

1 Answers1

1

The type tells you that get(allPokemon) might return an array or a Promise that resolves to an array. So what you should be able to do is await the response, then you can be sure that it's an array and not a promise. Then the type error should disappear, because it knows that the returned data is an array - then it knows that it has the filter() function.

export const pokemonAtom = atom(async (get) => {
  const search = get(searchAtom).toLowerCase();
  const all = await get(allPokemon);
  return all .filter((p) => p.name.toLowerCase().includes(search));
});

EDIT Another way is to use the useAtom instead of get that will resolve it for you. Then you don't need to make your function asyncronious.

export const pokemonAtom = atom((get) => {
  const search = get(searchAtom).toLowerCase();
  const [all] = useAtom(allPokemon)
  return all.filter((p) => p.name.toLowerCase().includes(search)); // Error
});
Nils Kähler
  • 2,645
  • 1
  • 21
  • 26
  • thanks! I have no more errors. I was also wondering if it is possible to make the atom (pokemonAtom) not async since I now need to some of the frontend code... – Yukinosuke Takada May 12 '23 at 10:45
  • @YukinosukeTakada Sure, look at the edited answer. – Nils Kähler May 12 '23 at 11:08
  • I tried the non asynchronous version but I got warning and it shows nothing. Warning: Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function. – Yukinosuke Takada May 12 '23 at 11:42