Search code examples
reactjstypescriptjotaitanstack

react: jotai, how to handle Promise inside atom()


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


Solution

  • 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
    });