Use react-query as a context provider for local data #489
-
React-query is very powerful when managing remote data fetching, but I also find that it's somehow very convenient to use it to share local state. I came up with a pattern for this use case, you can play with it here import React from "react";
import { useQuery, queryCache } from "react-query";
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Shared state using react-query</h1>
<Comp1 />
<Comp2 />
</div>
);
}
function useSharedState(key, initialValue) {
const { data: state } = useQuery(key, () => queryCache.getQueryData(key), {
initialData: initialValue
});
const setState = value => queryCache.setQueryData(key, value);
return [state, setState];
}
function Comp1() {
const [count, setCount] = useSharedState("count", 1);
console.log("comp1 rendered");
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
function Comp2() {
const [count, setCount] = useSharedState("count", 2);
console.log("comp2 rendered");
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
} Basically here react-query is used as a global state manager, I want to get some comments/suggestions for this pattern. Because I notice some unnecessary renders with this pattern, see below Each component is rendered 4 times and I don't know why this is happening, maybe related to the initial data? Thus I would like to ask if I use this pattern a lot in an app, is it going to have some perf issues? |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 12 replies
-
Anyone interested? |
Beta Was this translation helpful? Give feedback.
-
Though it works, this is not the use case |
Beta Was this translation helpful? Give feedback.
-
https://codesandbox.io/s/vigilant-jennings-j21km?file=/src/App.js |
Beta Was this translation helpful? Give feedback.
-
react-query is good at a manager for async state (promise based). Even if you just return data directly, you will still get:
what would you need all the good features of react-query for with client state? Background updates will needlessly re-render your component two times (isFetching transition), and you'd not want auto garbage collection either. So you'd need to customize your queryClient to opt out of everything that RQ gives you, by setting staleTime and cacheTime to infinity and you'd need to use initialData. And even then, you'd still have an inferior manager for synchronous state because you need to invent unique keys for no benefit.
For years, we've made the mistake of treating every state equally by treating our server data like client state. We've finally realized that this is sub-optimal, because they have different needs. Let's not make the same mistake again in the other direction by wanting to do everything with react query please :) |
Beta Was this translation helpful? Give feedback.
-
@gitcatrat I personally disagree with @TkDodo though I 👍 this article: https://tkdodo.eu/blog/react-query-as-a-state-manager
That's easy to suppress with
Create custom hooks to hold the complexity. function useAlerts() {
return useQuery(["_alerts"], async () => Promise.reject("...should never be called..."), {
initialData: [],
staleTime: Infinity, // this data/cache will be updated manually
cacheTime: Infinity, //
}).data
}
function Component1() {
const alerts = useAlerts()
// will be updated when alerts change
...
}
function Component2() {
const alerts = useAlerts()
// will be updated when alerts change
...
}
function Component3() {
// won't be updated when alerts change
const queryClient = useQueryClient()
React.useEffect(() => {
queryClient.setQueryData(["_alerts"], ...) // change global alerts
}, [])
...
}
I wouldn't call this solution "inferior" just because it doesn't have gimmicks like reactive vars. Every option that reduces a bundle size and a list of dependencies is at least worth considering. Cache keys are just paths to data fragments. Just a longer variant of smth. like In my experience React-Query is good at representing global states of easy-to-intermediate complexity. Consider that many people still use ad-hoc context-based global states which are objectively worse (...one shared subscription per context). To model, e.g. an interactive game state I would look for something more advanced. But many web apps don't need that. |
Beta Was this translation helpful? Give feedback.
Though it works, this is not the use case
react-query
has been built for. There are other libraries highly optimized for managing the state globally, here I list just a few:react-spring
team