🔎

urqlで任意のタイミングでqueryを実行する

2024/03/06に公開

結論

useQueryフックを使わず、代わりにuseClientフックを使うことで、任意のタイミングでワンオフクエリを実行することができます。

import {useClient} from 'urql';

const gqlClient = useClient();

const handleOnPress = async (): Promise<void> => {
  const variable = {
    input: {
      code,
    },
  };

  const result = await gqlClient.query(
    CodeVerifyDocument,
    variable,
  );
}

動機

urqlはuseQueryというフックを提供しているので、単純なデータフェッチであれば難しいことを考えずに実現することができます。

run-a-first-query

import { gql, useQuery } from 'urql';

const TodosQuery = gql`
  query {
    todos {
      id
      title
    }
  }
`;

const Todos = () => {
  const [result, reexecuteQuery] = useQuery({
    query: TodosQuery,
  });

  const { data, fetching, error } = result;

  if (fetching) return <p>Loading...</p>;
  if (error) return <p>Oh no... {error.message}</p>;

  return (
    <ul>
      {data.todos.map(todo => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
};

では、ユーザーの入力値をvariableとしてクエリしたい場合はどうすればいいのでしょうか?
例えばユーザが認証コードを入力して、送信ボタンを押したタイミングでサーバーサイドに正当性を確認するようなユースケースを想定します。

この場合useQueryの引数オブジェクトにpauseというフィールドを追加すれば値がtrueになるまでクエリの実行をしない制御ができます。

const [code, setCode] = useState('');
const [queryVariable, setQueryVariable] = useState({
  input: {code: ''},
});

const [_, executeQuery] = useQuery({
  query: CodeVerifyDocument,
  variables: queryVariable,
  pause: !queryVariable,
});

const handleSubmit = (event) => {
  event.preventDefault();
  setVariables({ input: {code} });
  executeQuery();
};

ただしこのコードには問題があります。
executeQueryの戻り値はvoidであり、なおかつawaitすることもできないため、「実行結果を待ってから画面遷移する」といったことができません。

解決

One-off Queries and Mutationsを参考に、graphql clientを取得してクエリを実行する方法があります。
冒頭のサンプルコードと同じになるのですが、urqlが提供しているuseClientフックを使ってclientを取得することで任意のタイミングでクエリを実行することができます。

import {useClient} from 'urql';

const gqlClient = useClient();

const handleOnPress = async (): Promise<void> => {
  const variable = {
    input: {
      code,
    },
  };

  const result = await gqlClient.query(
    CodeVerifyDocument,
    variable,
  );
}

1回きりのクエリで、データ更新を気にしなくていい場合はuseQueryよりもuseClientを使ったほうが便利です。

Discussion