Imagine this: you're building a React app, and suddenly, state management spirals out of control. You find yourself passing props down through a labyrinth of components, a process affectionately known as "prop drilling." It's like playing a game of telephone, but instead of whispers, you're passing data through multiple layers of components, and by the time it reaches the intended recipient, we all know how that game ends β with a big mess!
But fear not, dear React developers! There's a new kid on the block, and its name is useRQState (React Query State). This nifty hook is built on top of React Query and is designed to make local state management a breeze.
This powerful hook elegantly sidesteps the prop-drilling menace by allowing you to access and update state across your entire app using a single, magical query key.
How useRQState Works:
- Define Your State: Use
useRQState
to initialize your state, just like you would withuseState
. - Assign a Query Key: Give your state a unique identifier, the "query key."
- Access and Update: From any component, access and update the state using the same query key. It's like having a secret handshake that only components with the same key understand.
What's the Big Deal About useRQState?
Glad you asked! Here are just a few reasons why useRQState
is about to become your new best friend:
- Prop Drilling? Pshaw! Access and update state anywhere in your app without the dreaded prop-drilling.
- State Persistence Across Pages: Your state remains steadfast even as you navigate through your app, like a loyal companion.
- Simplicity Itself: useRQState mimics the familiar
useState
API, making it easy to adopt without a steep learning curve.
useRQState Implementation
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { queryClient } from "Utils/reactquery";
export function useRQState<T>(queryKey: string, initialData?: T | ((...args: any[]) => T)):
[ T, (update: Partial<T> | ((prevState: T) => Partial<T>)) => void, Omit<UseQueryResult<T>, "data"> ]
{
const { data, ...others } = useQuery({
queryKey: [queryKey],
initialData: () => {
if (initialData === undefined) {
const storedData = getQueryCache<T>(queryKey, true);
return storedData;
}
if (typeof initialData === 'function') {
const val = (initialData as CallableFunction)();
return val;
}
return initialData;
},
refetchInterval: false,
refetchOnReconnect: false,
refetchIntervalInBackground: false,
});
function setData(update: Partial<T> | ((prevState: T) => Partial<T>)) {
queryClient.setQueryData([queryKey], (prevState: T | undefined) => {
if (typeof update === 'function') {
return (update as (prevState: T) => Partial<T>)(prevState as T);
}
return update;
});
}
return [data, setData, { ...others }];
}
getQueryCache
const getQueryCache = <T>(queryKey: string, exact = false) => {
const [itemCache] = queryClient.getQueriesData<T>({
exact,
queryKey: [queryKey],
});
const [_queryKeys, data] = itemCache ?? [];
return data;
};
Utils/reactquery.ts
import { QueryClient } from "@tanstack/react-query";
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
});
Let's See It in Action!
import { useRQState } from "Hooks/useRQState";
function MyComponent() {
const [counter, setCounter] = useRQState("counter", 0);
return (
<button onClick={() => setCounter(count + 1)}>
Increment (with a touch of magic!)
</button>
);
}
In this example, we're using the query key "counter" to manage our count state. Any component that uses this key can access and update the count, regardless of its position in the component tree.
Updating State with Previous Values
Like useState, useRQState supports updating state based on the previous value, making it perfect for scenarios where you need to increment counters or manage complex state updates:
<button
onClick={() => setCounter((prev) => prev + 1)}
className="px-4 py-2 rounded-md bg-green-600 text-white flex-1"
>
Increment by 1
</button>
Ready to Ditch Prop Drilling?
With useRQState, managing state in your React app becomes a breeze. Say goodbye to spaghetti code and hello to a clean, maintainable, and scalable state management solution.
Want to see it in action? Check out our live demo or head over to the GitHub repo for the source code.
Demo & Source Code π
GitHub Repository: https://github.com/Nedum84/use-rq-state
Live Demo: https://nedum84.github.io/use-rq-state
Let's Connect π
- Twitter: @thenelson_o
- LinkedIn: Nelson Odo
What Do You Think? π
Share your thoughts! You can improve the code to serve your use case. Did this post help you?
Have ideas or feedback to share? Letβs continue the conversation in the comments below! π
Top comments (3)
Impressive work! I have curious questions tho:
I'm really happy you found it useful! π
useQuery
methods, including isLoading, refetch, etc., so you can use features likeinvalidateQueries
. However, it doesn't currently supportuseMutation
, but creating a new hook around it is definitely possible!This is just brilliant!