Identify the Problem
The first step to building anything is identifying a problem to target. I noticed a lot of devs were asking for guidance on integrating async queries into Zustand on reddit, github, and Stackoverflow. And I felt this exact pain myself. This is a real problem.
Investigate the Solutions
On the forums I was seeing solutions. They generally fell into two buckets - use fetch in an async function or use Tanstack Query. Time to investigate the solutions.
1) Use fetch in an async function
This solution works well out of the box and is simple. But it isn't feature-rich. Without caching, retries, data invalidation, etc. devs will quickly run into bad UX for their site. So this solution didn't cut it. I can do better.
const useStore = create((set) => ({
currentUser: '1', // Default user ID
setCurrentUser: (userId) => set({ currentUser: userId }),
data: null,
loading: false,
error: null,
// Action to fetch data
fetchData: async (userId) => {
set({ loading: true, error: null });
try {
const response = await fetch(`https://api.example.com/users/${userId}`).then(r.json());
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
const data = await response.json();
set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
// Example usage in a React component
function Component() {
const { data, loading, error, fetchData } = useStore();
useEffect(() => {
fetchData('https://api.example.com/data');
}, [fetchData]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h1>Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
2) Use Tanstack Query
Tanstack Query is a fantastic library. It's feature-rich. It's well-documented. But it's also complex and works very differently from Zustand. Dev's can use Zustand + Tanstack to build a great app. But I thought I can build a simpler solution if I integrate an async data handling library directly into Zustand. This is a strong solution. But Still I can do better.
// Create a Zustand store to manage the current user state
const useStore = create((set) => ({
currentUser: '1', // Default user ID
setCurrentUser: (userId) => set({ currentUser: userId }),
}));
function useFetchUserInfo(userId) {
return useQuery(
['userInfo', userId],
async () => await fetch(`https://api.example.com/users/${userId}`).then(r.json())
);
}
function Component() {
const { currentUser, setCurrentUser } = useStore();
const { data: userInfo, error, isLoading } = useFetchUserInfo(currentUser);
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>User Information</h1>
<span>{userInfo.name}</span>
</div>
);
};
Set MVP Goals
Now I've looked at the problem. I've looked at the solution. I believe I can build a better solution. Time to set goals to get something built quickly. My goal is simple - build an async data handling library for Zustand that works well for basic usage, has a path to become feature-rich, and is simple to integrate. And build it quickly.
Plan
At this point it's temping to create a well though-out plan. Building an async library is a big task. And without a good plan I'll probably build messy solution. But the truth is I don't have enough information to plan effectively yet. I am going to learn a lot as I build. It's important that I stay flexible and adapt to new information. When I have more information I'll plan more effectively.
Build
The fun part! Now it's time to build. Just start coding with my goal in mind. Be open to new ideas as they come in. And put a timebox on it - I chose 1 month. Remember, the goal is to build a first version quickly. Then I can get more information through feedback quickly.