With all the power that apollo-client brings to your application development and environment, there are very minimal challenges that you need to answer, like managing variables of a graphql query.
As you obviously know query variables are equivalent to the REST's post method payload, which you would've sent to the server as parameters which result may heavily depend on.
Let's go about a real-life practical example, here's a query variables object we might embark on:
{
"limit": 10,
"offset": 0,
"search": "",
"filters": {
"vendors": [],
"contracts": []
}
}
We have limit
and offset
responsible for pagination, search
as the keyword to filter a list(returned by the API for sure), and filters
which is responsible to filter out items by their other relations or attributes. So three fields( maybe more if you consider filters
children) and each of them controlled by totally separate components, meaning three different *ChangeHandler
s.
If you come from a non-apollo/graphql background as I do, you'd probably go about managing this object with some solution like react's very useReducer
hook.
It would look something like this:
// component.js
const variablesInitialState = {
"limit": 10,
"offset": 0,
"search": "",
"filters": {
"vendors": [],
"contracts": []
}
}
//
function variablesReducer(state, action) {
switch (action.type) {
case 'setSearch':
//non-relevant logic
//...
case 'setPagination':
//non-relevant logic
//...
case 'setFilters':
//non-relevant logic
//...
default:
return state;
}
}
// ...
function Component(props) {
const [variablesState, variablesDispatch] = useReducer(
variablesReducer,
variablesInitialState,
);
// ...
const {
data: teamMembersData,
networkStatus: membersNetStat,
refetch: membersRefetch,
} = useQuery(GET_TEAM_MEMBERS, {
variables: {
...variablesState,
})
}
As much as it may seem logical to do so, but the maintenance overhead will accumulate over time while you are changing your state and then call refetch
[too], every time.
So maybe I could quit being explicit about calling refetch
while it seems to be the most probable next move.
So that's where Apollo-Client's Reactive Variables
comes into play.
As it's put by Apollo Blog Reactive Variables
are:
containers for variables that we would like to enable cache reactivity for. Using the small API, we can either: 1- set the value by passing in an argument — var(newValue) 2- get the value by invoking the reactive variable — const currentValue = var()
Utilizing that we could create a little hook containing the logic for managing the variables state like so:
// hook.js
import { makeVar } from '@apollo/client';
// Create the initial value
const tmqvarsInitial = {
search: '',
filters: { vendors: [] },
limit: 20,
offset: 0,
};
// Create the teamMembersQuery var and initialize it with the initial value
export const teamMembersQVars = makeVar(tmqvarsInitial);
// expose the operations done on the state
export function useTeamMemberQVars(teamMembersQVars) {
const setSearch = text => {
const tmqvars = teamMembersQVars();
const updatedTmqvars = {
...tmqvars,
search: text,
};
teamMembersQVars(updatedTmqvars);
};
const setFilters = filters => {
const tmqvars = teamMembersQVars();
const updatedTmqvars = {
...tmqvars,
filters,
};
teamMembersQVars(updatedTmqvars);
};
const setPagination = ([limit, offset]) => {
const tmqvars = teamMembersQVars();
const updatedTmqvars = {
...tmqvars,
limit,
offset,
};
teamMembersQVars(updatedTmqvars);
};
return {
setSearch,
setFilters,
setPagination,
};
}
to be used like:
// component.js
import { useReactiveVar } from '@apollo/client';
import { teamMembersQVars, useTeamMemberQVars } from './useTeamMembersQVars';
// ...
function Component(props) {
// ...
// reactive variables
const { setSearch, setFilters, setPagination } = useTeamMemberQVars(
teamMembersQVars,
);
// ...
// query def
const { data, error, networkStatus } = useQuery(GET_TEAM_MEMBERS, {
variables: useReactiveVar(teamMembersQVars),
});
}
}
This way you just worry about calling operations (e.g. setSearch
) inside your *ChangeHandler
s and your query will rerun automatically because the hook useReactiveVar
from Apollo-Client will rerender the components if the reactive variable accompanied by them has been through a change.
P.S.
This is just a solution to a challenge that may have thousands of solutions. I am a beginner and grateful for your suggestions. Please do not hold back.
Cheers,
To all who never quit being a beginner. 🍻 🍻
Top comments (2)
Hey, excellent stuff! I just started a new app using apollo client 3 and automatically reached for redux for local state. Considering switching to "reactive vars", but had two concerns:
I think your solution above looks great, good job :)
Thank you for this. Was looking for an example on how to use a reactive var as a variable when calling an API. Now I know the key is
useReactiveVar