You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jotai 2.x中atomsWithQuery返回Promise引发类型错误的解决方法

修复jotai-tanstack-query新版本中atomsWithQuery返回类型导致的TS错误

这个问题是因为jotai-tanstack-query从0.4.x升级到0.7.x后,atomsWithQuery的返回行为发生了变化:新版本默认返回的原子值可能是已解析的数组或Pending状态的Promise,而旧版本会自动处理异步状态直接返回数组类型。针对这个类型不匹配问题,有几种简洁的修复方式:

方案1:使用React Suspense处理异步状态(推荐)

Jotai 2.x和新版jotai-tanstack-query原生支持React Suspense,只需用Suspense包裹使用该原子的组件,useAtomValue就会直接返回已解析的Pokemon[]类型,无需处理Promise:

import { Suspense } from 'react';
import { useAtomValue } from 'jotai';
import { allPokemonAtom } from './your-atom-file';

function PokemonList() {
  // 当组件在Suspense内时,allPokemon直接是Pokemon[]类型
  const allPokemon = useAtomValue(allPokemonAtom);
  const filteredPokemon = allPokemon.filter(pokemon => pokemon.name.includes('pika'));

  return (
    <ul>
      {filteredPokemon.map(pokemon => (
        <li key={pokemon.id}>{pokemon.name}</li>
      ))}
    </ul>
  );
}

// 在父组件中用Suspense包裹
function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <PokemonList />
    </Suspense>
  );
}

方案2:用Jotai的loadable处理异步状态(无需Suspense)

如果你不想使用Suspense,可以用Jotai提供的loadable工具将异步原子转换为包含状态的同步原子,明确处理加载、成功、失败三种状态:

import { useAtomValue } from 'jotai';
import { loadable } from 'jotai/utils';
import { allPokemonAtom } from './your-atom-file';

// 将异步原子转换为可加载状态的原子
const allPokemonLoadable = loadable(allPokemonAtom);

function PokemonList() {
  const { state, data, error } = useAtomValue(allPokemonLoadable);

  if (state === 'loading') return <div>加载中...</div>;
  if (state === 'hasError') return <div>加载失败:{error?.message}</div>;
  
  // 此时data一定是Pokemon[]类型,可安全调用filter
  const filteredPokemon = data.filter(pokemon => pokemon.name.includes('pika'));

  return (
    <ul>
      {filteredPokemon.map(pokemon => (
        <li key={pokemon.id}>{pokemon.name}</li>
      ))}
    </ul>
  );
}

方案3:手动类型守卫(不推荐,繁琐)

如果以上两种方案都不适用,可以在代码中添加类型守卫,确保调用filter前值不是Promise:

import { useAtomValue } from 'jotai';
import { allPokemonAtom } from './your-atom-file';

function PokemonList() {
  const allPokemon = useAtomValue(allPokemonAtom);

  // 类型守卫:判断是否为已解析的数组
  if (Array.isArray(allPokemon)) {
    const filteredPokemon = allPokemon.filter(pokemon => pokemon.name.includes('pika'));
    return <ul>{filteredPokemon.map(...)}</ul>;
  } else {
    // 处理Promise pending状态,比如显示加载中
    return <div>加载中...</div>;
  }
}

内容的提问来源于stack exchange,提问作者Yukinosuke Takada

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 23:03:11