'How to let Query are performed at the component onmount and triggered by user event later?

My page has a search form and table with a paginator. So I have two states: searchCondition and pagination. I need to query the channelList when the page component is mounted.

When the user clicks the paginator, the pagination state will be changed so that a new channelList query will be made. That's ok.

The search form includes two input form items and a "Query" button. The channelList query also can be triggered by the user click event.

But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

I read the doc Dependent Queries, there is an enabled option, but don't think it can solve my issue.

Currently:

  • Send the query on the component mount. - done!
  • Made a new query when pagination state changed - done!
  • User types something in the input form item(this will cause the searchCondition state changed) and clicks the "Query" button, then made a new query - undone.

When sending the channelList query, I will make the API parameters by merging searchCondition and pagination states.

const { searchCondition, setSearchCondition } = useSearchCondition(initialSearchCondition);
const { pagination, setPagination } = usePagination();
const channelListQuery = useQuery(
  ['channelList', pagination, searchCondition],
  async () => {
    const getChannelListParams = omitFalsyFields({
      currentPage: pagination.current,
      pageSize: pagination.pageSize,
      ...searchCondition,
    });
    const { code, result } = await apis.channel.getChannelList(getChannelListParams);
    if (code === '0') {
      return result;
    }
  },
  { initialData: { totalItem: 0, resultList: [] }, refetchOnWindowFocus: false, keepPreviousData: true },
);


Solution 1:[1]

But now, when the user types something in the input form item. The channelList query is made immediately when the searchCondition state changed. That's not expected. I want this query triggered when the user clicks the "Query" button.

What you need for this to happen is to have two state. One inside the form so that users can type (this can also be an uncontrolled form), and another one that you set when the user clicks the "Query" button.

That second state is what your query should "listen to" - by being part of the query key. Pseudo code:

function App() {
 const [search, setSearch] = useState()
 const { data } = useQuery(['channelList', search], () => getChannelList(search), { enabled: !!search }

 <SearchFrom onSubmit={setSearch} />
}

by making sure to only call setSearch when the user clicks the Query button, you make sure that the query doesn't fire on every click.

A good place to store "applied searches" is also the url: When the user clicks the button, you write to the url, and the query subscribes to that.

This also makes your urls sharable, which is nice.

I have a short example on that here:

https://codesandbox.io/s/react-query-url-filtering-h0ikw?file=/src/App.tsx

The filter form itself is uncontrolled - but it doesn't really matter how you solve it, it's "internal" to the filter form. The "global" client state lives in the url.

Solution 2:[2]

Found a way Disabling/Pausing Queries, but I am not sure it's the best way to handle this situation. Set the enabled option to false to tell the query don't execute automatically (onMount and onDidUpdate). We will fetch the query completely manually by using refetch method(A function to manually refetch the query.)

By using this way, I have to add another useEffect hook in order to fetch the query when the component mount. I am looking for a way just to use useQuery.

//...

const channelListQuery = useQuery(
    ['channelList', pagination, searchCondition],
    async () => {
      const getChannelListParams = omitFalsyFields({
        currentPage: pagination.current,
        pageSize: pagination.pageSize,
        ...searchCondition,
      });
      const { code, result } = await apis.channel.getChannelList(getChannelListParams);
      if (code === '0') {
        return result;
      }
    },
    { initialData: { totalItem: 0, resultList: [] }, refetchOnWindowFocus: false, keepPreviousData: true, enabled: false },
  );

// onMount
useEffect(() => {
  channelListQuery.refetch();
}, [pagination])

// User click "Query" button
const handleSearchFormSubmit = () => {
  channelListQuery.refetch();
}

// ...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TkDodo
Solution 2