In today’s rapidly shifting web development arena, the techniques and tools we use to manage state in our applications are in the spotlight more than ever. During my time at a previous company, we heavily relied on Redux coupled with Saga.
While powerful, it came with an intricate boilerplate, leading to a significant tech debt that felt like a chain around our development pace. Global state management tools, which we once hailed as the beacon of streamlined state management, started to feel cumbersome and overbearing.
If this resonates with you, and you’ve found yourself navigating the dense forest of global state, tangled in the vines of Redux and its contemporaries, there’s a silver lining.
Today, we’ll explore the dynamic duo of Zustand and React Query. These two libraries might just be the caped crusaders ready to rescue you from the clutches of unwieldy state management.
Why Bother with Both?
Let’s face it, we’ve all been there — tangled up in the web (pun intended) of state management and data-fetching woes. With the emergence of React Query and Zustand, we’re stepping into a realm that feels almost enchanting. Zustand brings simplicity to global state, sidestepping the usual reducers, while React Query takes the lead in efficient asynchronous data fetching. Together, they’re the Dumbledore and McGonagall of your React project, expertly guiding your code through the dark arts of state management.
Zustand: The State Charmer
Zustand, German for “state”, brings the charm of simplified state management to the party. While it’s tempting to dip everything into a global state fondue, Zustand advocates for a more selective approach. The real zinger? Zustand allows for nimble state modifications, giving developers a streamlined method to handle data without the clutter. It’s like having a state management concierge, guiding you to make the most efficient choices
// spellbookStore.js
import create from 'zustand';
const useSpellbookStore = create((set) => ({
spells: [],
addSpell: (spell) => set((state) => ({ spells: [...state.spells, spell] })),
setSpells: (spells) => set({ spells }),
}));
export default useSpellbookStore;
import useSpellbookStore from './spellbookStore';
// Add a spell to the spellbook
const { addSpell, spells } = useSpellbookStore();
addSpell("Lumos");
console.log(spells); // ["Lumos"]
React Query: The State Trooper
When it comes to fetching, caching, or synchronizing data, React Query is your one-stop-shop. At first glance, you’d think it’s just an async query/mutation manager, but dig deeper and you’ll find it’s essentially a global state manager in disguise. No capes though, just pure code goodness!
The real beauty lies in its built-in query data selectors. Need quick access to fetched data in a transformed format? These selectors have got you covered. And the best part? You can abstract your query hooks and selectors into custom hooks for easy use throughout your component trees, effectively reducing the need for a separate store.
Starting off with React Query is as straightforward as setting up a QueryClient and wrapping your app with the QueryClientProvider.
// App.js
import { QueryClient, QueryClientProvider } from 'react-query';
import SpellbookComponent from './SpellbookComponent';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<UserComponent />
</QueryClientProvider>
);
}
export default App;
The Dream Team in Action
Imagine building a typical CRUD application. React Query would handle your asynchronous tasks, fetching data, and caching them efficiently. Need to transform this data for a specific component? Just swing by React Query’s selectors. Zustand, on the other hand, would hold onto user preferences, UI state, or other pieces of state that don’t necessarily derive from asynchronous operations. The outcome? A project with a well-defined data flow, fewer bugs, and yes, a lot fewer hairs pulled out in frustration.
// SpellbookComponent.js
import { useQuery } from 'react-query';
import useSpellbook from './spellbookStore';
function SpellbookComponent() {
const { setSpells } = useSpellbook();
const fetchSpells = async () => {
const response = await fetch('/api/spells');
if (!response.ok) {
throw new Error('Could not fetch spells from the spellbook');
}
return response.json();
};
const { data: spells, isError, isLoading } = useQuery(
'spells',
fetchSpells,
{
onSuccess: (fetchedSpells) => {
setSpells(fetchedSpells);
},
}
);
if (isLoading) return <div>Consulting the spellbook...</div>;
if (isError) return <div>Error retrieving spells</div>;
return (
<div>
<h2>Your Spellbook</h2>
<ul>
{spells?.map((spell, index) => (
<li key={index}>{spell}</li>
))}
</ul>
</div>
);
}
export default SpellbookComponent;
Both Zustand and React Query have their unique strengths. While you can get by with just one of them, together, they create a state management utopia. Sure, using them might not grant you web-slinging abilities, but they sure will make your app development process feel super. If you’re looking for a state management strategy that’s truly out-of-the-box (or should I say out-of-the-state?), this duo is your best bet.
Remember, in the world of React, staying updated is not just a state of mind but a state of the art. And with React Query and Zustand, you’re bound to be in a state of bliss!
Top comments (3)
I think you can extract the useQuery and then reuse it elsewhere hence you don't actually need another state to keep "fetchedSpells"?
This duo is awesome!!
Awesome!! What I was searching for!!