🔎
urqlで任意のタイミングでqueryを実行する
結論
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というフックを提供しているので、単純なデータフェッチであれば難しいことを考えずに実現することができます。
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