I have seen a pattern from the react-query
library which does the following:
(OOP)
// it instantiates an object from a class
const queryClient = new QueryClient()
(Hooks)
// Then it passes this object as a prop to the provider
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
and in the <Todos />
component, this library gives you the option to both use the client or use a hook.
// Todos
const client = useQueryClient(); // (Hook)
const query = useQuery( key, fn, option ); // (Hook)
What I cannot understand is how this library is able to keep its state in sync between a normal javascript class object namely the client
and the usage of other hooks. What I mean by that is for example, you are able to use the hook useQuery
with a key
as the first argument and what this hook does is that executes the fn
function and stores the result in a data
property inside the query
.
The funny part is that you can then use this data
by calling the following method from the client
.
const { data } = client.getState(key);
So what it actually does behind the scenes in order to keep in sync both a classic javascript object and hooks ?
const client = useQueryClient(); // (Hook)
const query = useQuery( key, fn, option ); // (Hook)
// ----> in sync : query.data === client.getState(key).data
CodePudding user response:
Its because the source of truth is that query-client object. It contains the caches (also classes) and a bunch of methods to access them and so on.
I suppose you could instantiate it in some module, and then import it and access it if all you wanted to do was to hold mutable state in some object somewhere. But part of the charm is to marry the calling of cached functions and access of that state to react: Have the queries be called when a component mounts, and have other components that are interested in the same result all wait for the same result. Have newly mounted components investigate if the data is stale. Refetch data on a timer, but only one for all components, and so on and so on.
The integration into the react lifecycle functions via hooks (all modern react libraries with self respect gotta have some hooks!), because they can slip into your components lifecycle in a simple and declarative way: "use" some resource.
These hooks then employ reacts lifecycle hooks, and the cache and methods in the QueryClient. Because the QueryClientProvider provides the queryClient through a react context, you don't have to export/import your instantiated QueryClient throughout your modules, they find it themselves.
There's a lil trick in the module top-level defining the react context here
I made a little codesandbox you can check out here
If you want to brush up on the observer pattern you can do so here
And compare with this walkthrough on app state without context.
Also, don't forget how using object references as function parameters can have side effect.