7

I am using React Apollo to get data from my server. When my page loads I am using useQuery to retrieve the data. This works fine. The problem is when I make a change to the search form, this updates the state which causes an unwanted re-render which calls the server again.

I want to call the server only when the page loads and when I click the search button.

useQuery:

const { loading: loadingLessons, error: lessonsError, data: lessons } = useQuery(
 LESSON_BY_PARAMS,
    {
      variables: { findLessonsInput: searchParams },
    },
);

When I change a form field it calls updateFormField which updates the redux state causing a re-render

<Autocomplete
  options={categories}
  getOptionLabel={(option: any) => option.label}
  inputValue={form.category}
  defaultValue={() => {
    const value = categories.find(option => option.label === form.category);
    return value;
  }}
  onChange={(event, value) => updateFormField('category', value?.label)}
  renderInput={params => (
    <TextField {...params} label="Category" variant="outlined" fullWidth />
  )}
/>

I am using react hooks.

Dharman
  • 30,962
  • 25
  • 85
  • 135
AngularBoy
  • 1,715
  • 2
  • 25
  • 35

3 Answers3

20

Have a look at the skip option which can be used to entirely skip the query. You can do something like this:

const [skip, setSkip] = React.useState(false)
const { loading, data } = useQuery(QUERY, { skip })

React.useEffect(() => {
  // check whether data exists
  if (!loading && !!data) {
    setSkip(true)
  }
}, [data, loading])

So, once data returned you simply set skip option to true. If you want to make a request you should handle onClick on the search button(simply setSkip(false)).

Vadim Sheremetov
  • 523
  • 1
  • 4
  • 14
0

If you use pure apollo client you will loose some features from apollo. There is no need to move graphql call into redux action, you can use selectors in Redux to prevent components from reloading or making unnecessary calls to a server.

Selectors in Redux

Petr Schukin
  • 170
  • 2
  • 12
-3

Thanks for the suggestion @Vadim Sheremetov however I decided to move the api call to a redux-thunk function:

When the page loads I dispatch an action passing it search params and apollo client:

const client = useApolloClient();

useEffect(() => {
    loadLessons(searchParams, client);
}, [location.search]);

const mapDispatchToProps = dispatch => {
  return {
    loadLessons: (searchParams, client) =>
      dispatch(loadLessonsAction(searchParams, client)),
  };
};

The action invokes a redux thunk function which then dispatches another action that updates the state:

actions.ts:

export function loadLessonsAction(searchParams: any, client: ApolloClient<object>): 
any {
  return async function(dispatch) {
    try {
      const { data } = await client.query({
        query: LESSON_BY_PARAMS,
        variables: { findLessonsInput: searchParams },
      });
      dispatch(loadLessonsSuccessAction(data.lessonsByParams));
    } catch (error) {}
  };
}

export function loadLessonsSuccessAction(lessons: any[]): FilterPanelActionTypes {
  return {
    type: LOAD_LESSONS_SUCCESS,
    payload: lessons,
  };
}

reducer.ts:

case LOAD_LESSONS_SUCCESS: {
  return {
    ...state,
    lessons: action.payload.lessons,
    lessonCount: action.payload.count,
  };
}
AngularBoy
  • 1,715
  • 2
  • 25
  • 35