4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

useQueryをクリックイベントで使用する方法

Last updated at Posted at 2022-10-24

Nextjs(やReactjs)で開発している中で、React Query の useQuery を使うと結果をキャッシュ出来て便利です。

ただuseQueryをonClick時などに使いたい場合、コンポーネントの中の関数内で実行することになります。hooksのルールに反しているのでエラーが発生してしまいます。

Hooksのルール

Hooksにはルール(Breaking the Rules of Hooks)があります。

紹介されているGoodケース

function Counter() {
  // ✅ Good: top-level in a function component
  const [count, setCount] = useState(0);
  // ...
}

function useWindowWidth() {
  // ✅ Good: top-level in a custom Hook
  const [width, setWidth] = useState(window.innerWidth);
  // ...
}

紹介されているBadケース

function Bad1() {
  function handleClick() {
    // 🔴 Bad: inside an event handler (to fix, move it outside!)
    const theme = useContext(ThemeContext);
  }
  // ...
}

function Bad2() {
  const style = useMemo(() => {
    // 🔴 Bad: inside useMemo (to fix, move it outside!)
    const theme = useContext(ThemeContext);
    return createStyle(theme);
  });
  // ...
}

class Bad3 extends React.Component {
  render() {
    // 🔴 Bad: inside a class component
    useEffect(() => {})
    // ...
  }
}

ダメなケース

ルールに反するので、関数コンポーネントのトップレベルで使用せずに、イベントハンドラーで実行するとエラーになります。

// イベントハンドラー内で読んでしまっているBadケース
import { FC } from "react";
import { useQuery } from "react-query";

const SampleComponent: FC = () => {
  const handleClick = async () => {
    const { data } = useQuery(
      ["sample-query-key"],
      async () => await fetch("/api/sample-fetch").then((res) => res.json()),
      {
        enabled: false,
      }
    );
  };

  return (
    <div>
      <h1>Favorite MANGA</h1>
      <button onClick={handleClick}>Show Manga</button>
      {/* {data && (
        <>
          <h1>{data.title}</h1>
          <div>
            <img src={data && data.imgSrc} width="250px" height="250px" />
          </div>
        </>
      )} */}
    </div>
  );
};

export default SampleComponent;

image.png
https://reactjs.org/warnings/invalid-hook-call-warning.html

とはいえ、トップレベルに記載すると、初回レンダリング時に実行されるので、クリックイベントと連動させることができません。

// 通常の書き方ではクリックイベントと連動しない
import { FC } from "react";
import { useQuery } from "react-query";

const SampleComponent: FC = () => {
  const { data } = useQuery(
    ["sample-query-key"],
    async () => await fetch("/api/sample-fetch").then((res) => res.json())
  );

  const handleClick = async () => {};

  return (
    <div>
      <h1>Favorite MANGA</h1>
      <button onClick={handleClick}>Show Manga</button>
      {data && (
        <>
          <h1>{data.title}</h1>
          <div>
            <img src={data && data.imgSrc} width="250px" height="250px" />
          </div>
        </>
      )}
    </div>
  );
};

export default SampleComponent;

イベントと連動させて再フェッチする方法

useQueryにはrefetchがあり、任意のタイミングでフェッチさせることができます。refetch関数をイベントハンドラー内で実行すればOKです。

クリックイベントに連動させる場合は下記のような感じです。

import { FC } from "react";
import { useQuery } from "react-query";

const SampleComponent: FC = () => {
  const { data, refetch } = useQuery(
    ["sample-query-key"],
    async () => await fetch("/api/sample-fetch").then((res) => res.json()),
    {
      enabled: false,
    }
  );

  const handleClick = async () => {
    refetch();
  };

  return (
    <div>
      <h1>Favorite MANGA</h1>
      <button onClick={handleClick}>Show Manga</button>
      {data && (
        <>
          <h1>{data.title}</h1>
          <div>
            <img src={data && data.imgSrc} width="250px" height="250px" />
          </div>
        </>
      )}
    </div>
  );
};

export default SampleComponent;

まずuseQueryの返り値からrefetchを取り出し、handleClick関数で実行しています。

ポイントは useQuery の第3引数で設定している enabled: false になります。

これはつけなくてもいいのですが、falseにしない場合は、初回レンダリング時に自動的にfetchされてしまいます。あくまでボタンなどのイベントが発生したときにのみfetchしたい場合は false にしておきます。

usequery-refetch (2).gif

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?