Managing Query Variables State via Apollo-Client's "Reactive Variables"
Tags: react, graphql, apolloclient, statemangement
Published: 2020-11-08 16:16:33

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:

```json
{
    "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:

```javascript
// 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](https://www.apollographql.com/blog/local-state-management-with-reactive-variables/) `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:
```javascript
// 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:
```javascript
// 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. :beers: :beers: