Jotai状态管理实战经验与性能优化技巧分享

司马艺童 框架 阅读 920
赞 10 收藏
二维码
手机扫码查看
反馈

又踩坑了,Jotai 的异步状态管理让我头大

最近在用 Jotai 做一个项目的状态管理,本来觉得这东西简单好用,结果在处理异步数据的时候踩了个大坑。折腾了半天才发现问题出在哪。

Jotai状态管理实战经验与性能优化技巧分享

场景是这样的:我需要从 https://jztheme.com/api/data 拉取一些数据,然后把数据存到 atom 里供组件使用。听起来挺简单的对吧?但问题就出在异步这块。

核心代码就这几行,但一开始完全跑不通

我最初写的代码大概是这样的:

import { atom, useAtom } from 'jotai';

const dataAtom = atom(async () => {
  const res = await fetch('https://jztheme.com/api/data');
  return res.json();
});

function DataComponent() {
  const [data] = useAtom(dataAtom);

  if (data instanceof Promise) {
    return <div>加载中...</div>;
  }

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

这里我踩了个坑,以为直接在 atom 定义里写 async 就能搞定异步逻辑。结果页面一直显示“加载中…”,根本没渲染出实际数据。

排查过程:试了三种方法才找到正解

一开始我以为是 API 的问题,跑去 Postman 测试了一下接口,发现返回的数据是正常的。这就排除了后端的问题。

后来试了下把 async 放到组件内部:

const dataAtom = atom(null);

function DataComponent() {
  const [data, setData] = useAtom(dataAtom);

  useEffect(() => {
    fetch('https://jztheme.com/api/data')
      .then(res => res.json())
      .then(setData);
  }, [setData]);

  if (!data) {
    return <div>加载中...</div>;
  }

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

这个方案倒是能跑通,但总觉得不够优雅。而且如果多个组件都需要用这个数据,就得重复写这些逻辑。

最终解决方案:atomWithDefault + atomWithQuery

最后还是得感谢社区,找到了一个更合适的解决方案。Jotai 提供了一个叫 atomWithDefault 的工具,配合自定义的异步处理特别好用。

完整代码如下:

javascript
import { atom, useAtom, atomWithDefault } from 'jotai';
import { atomWithQuery } from 'jotai/query';

const fetchData = async () => {
const res = await fetch('https://jztheme.com/api/data');
if (!res.ok) throw new Error('Failed to fetch data');
return res.json();
};

const dataAtom = atomWithDefault(async get => {
return fetchData();
});

function DataComponent() {
const [data, setData] = useAtom(dataAtom);

if (data instanceof Promise) {
data.then(setData).catch(err => console.error(err));
return <div>加载中...</div>;
}

return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
`>

这里有几个关键点要注意:

  • atomWithDefault 可以让 atom 在初始化时执行异步函数
  • 记得处理异常情况,否则某个请求失败可能导致整个应用崩溃
  • 在组件里要判断返回值是否是 Promise,避免直接渲染未解析的数据

技术细节补充:为什么第一种写法会出问题

回过头来看最初的写法,问题出在 Jotai 的 atom 设计上。atom 本质上是一个同步的状态容器,虽然你可以传入一个返回 Promise 的函数,但它不会自动帮你处理异步逻辑。

换句话说,当你在 atom 定义里写了 async 函数,返回的其实是一个 Promise 对象,而不是实际的数据。所以组件拿到的始终是个 Promise,自然没法正常渲染。

踩坑提醒:这三点一定注意

总结一下这次踩坑的经验,有几点需要注意:

  • Jotai 的 atom 默认是同步的,异步逻辑需要额外处理
  • 不要直接在组件里写 fetch 逻辑,尽量封装到 atom 层
  • 记得处理异常情况,比如网络错误或数据格式不对

虽然改完之后还有个小问题——首次加载时会有短暂的闪烁(因为先显示“加载中…”再渲染数据),但整体效果已经可以接受了。如果你有更好的优化方案,欢迎评论区交流!

以上是我踩坑后的总结,希望对你有帮助。后续我会继续分享一些 Jotai 的实战经验,尤其是复杂状态管理的场景。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论