ike81818
@ike81818

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

useQueryのonSuccess内の処理を分岐したいです

解決したいこと

ページングのために、
「次へ」ボタン(PC用)
「次のデータを見る」ボタン(スマホ用)
を設置したいと思っています。

外部データの取得にあたっては、useQueryを使用し、
「次へ」ボタンをクリックしたときは、取得したデータをitemsに上書きし、
「次のデータを見る」ボタンをクリックしたときは、itemsに累積追加し、
それを、ブラウザーに表示したいと考えています。

一応、flagというstateを作って、
「次へ」ボタンをクリックしたとき、setFlag(1)
「次のデータを見る」ボタンをクリックしたとき、setFlag(2)として、
onSuccess内において、flagの値で条件分岐させると、
一見それらしく動くのですが、早く何回もクリックすると、挙動がおかしくなります。

そこで、

const flg;  というパラメータを設定して、
こちらを条件分岐に使いたいと思うのですが、onSuccess内に渡すことができません。

何か良い方法はないでしょうか?

[サイト] https://willowy-pixie-73a101.netlify.app/

コード

pages/index.js
import styles from "../styles/Home.module.css";
import { useState } from "react";
import { useQuery } from "react-query";
import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      refetchOnWindowFocus: false,
    },
  },
});

export default function Home() {
  return (
    <QueryClientProvider client={queryClient}>
      <Main />
    </QueryClientProvider>
  );
}

function Main() {
  const [page, setPage] = useState(1); //現在のページ
  const [items, setItems] = useState([]); //アイテムの配列
  const [pageCount, setPageCount] = useState(""); //総ページ数
  const [flag, setFlag] = useState(1);

  const options = {
    params: {
      page: page,
    },
  };
  let flg = 1;

  const {
    data: data1,
    isLoading: isLoading1,
    isError: isError1,
    error: error1,
    refetch: refetch1,
  } = useQuery(
    ["items1"],
    async () => {
      const urlSearchParam = new URLSearchParams(options.params).toString();
      const defaultEndpoint = "/api/items?" + urlSearchParam;
      return await fetch(defaultEndpoint).then((res) => res.json());
    },
    {
      enabled: true,
      onSuccess: (data1) => {
        if (flg == 1) {           // constを使用
        // if (flag == 1) {       // stateを使用
          setItems(data1.users.items); //データを上書きセット
        } else {
          setItems((prev) => {                  //データを累積セット
            return [...prev, ...data1.users.items];
          });
        }
        setPageCount(data1.users.pageCount);
      },
    }
  );
  if (isLoading1) return <span>Loading...</span>;
  if (isError1) return <span>Error:{error1.message}</span>;

  const getItems = async () => {
    refetch1();
  };

  // Pagination +
  const pageIncrement = () => {
    if (page < pageCount) {
      const newPage = page + 1;
      setPage((prevPage) => prevPage + 1);
      options.params.page = newPage;
      flg = 1;
      setFlag(1);
      getItems();
    }
  };

  // Readmore +
  const pageReadmore = () => {
    if (page < pageCount) {
      const newPage = page + 1;
      setPage((prevPage) => prevPage + 1);
      options.params.page = newPage;
      flg = 2;
      setFlag(2);
      getItems();
    }
  };

  return (
    <>
            {items.map((item, index) => {
      <div key=(index)>
         {item}
      </div>
      }              

      //レスポンシブデザイン CSSで片方のみ表示させる
            //PC用のボタン
      <button className={styles.nextButton} onClick={() => pageIncrement()}>次へ</button>
      //スマホ用のボタン
      <button className={styles.readMoreButton} onClick={pageReadmore}>次のデータを見る</button>
    </>
  );
}
styles/Home.module.css
/* PC設定 */
.readMoreButton {
 display: none;
}

/* タブレット設定 */
@media screen and (max-width: 800px) {
 .nextButton {
  display: none;
 }

 .readMoreButton {
  display: block;
 }
}
0

4Answer

PCかモバイルかどうかはページをロードした時点で確定すると思うので、useStateは使う必要がないのかなと思います。
それならflagとして1または2をconstで書けるんじゃないでしょうか?
それならonSuccessの関数にも渡せる気がしますが

1Like

Comments

  1. @ike81818

    Questioner

    ご回答ありがとうございます。

    onSuccess内の条件分岐をlet flg に変えて動作確認を行ったところ、
    各ボタンをクリックしたときのflg=1,flg=2は、反映されず、
    初期設定のlet flg=1のままでは動作します。
    なので、onSuccess内に初期値let flgの値は渡せているようです。

    ただ、PCとモバイルの切り分けは、
    @media screen and (max-width: 800px) {
    を使って、
    display: none; やdisplay:block;することしか知識がありません。

    if (PCの場合) {        //PCの場合
    const flg = 1;
    } else {                    //モバイルの場合
    const flg = 2;
    }
    のような処理を
    index.jsに書くことができるのでしょうか?

    ご教示いただけますと幸いです。
  2. { ... } にconstを書いても この{ ブロック } 内でしかスコープがないので...
    const flag = isMobile() ? 2 : 1
    とかが良いのかなと思います。

ユーザーエージェントを調べたり、モバイル端末にだけあるイベントを調べたり色々出来ますが、自分はこれを使ってます。

const isMobile = () => {
  const touchPoints = "maxTouchPoints" in navigator ? navigator.maxTouchPoints : 0
  return touchPoints > 0
}

npmパッケージもゴロゴロあります。

1Like

多分それはSSRが動いている時にnavigatorを参照しているから?かなと。
単純にnpmパッケージを探してどれか使えばいいと思いますよ。

1Like

ご回答ありがとうございます。
上記の例を使わせていただいたのですが、
「navigator is not defined」とエラーが出てしまい、Next.jsだと、
Dynamic Importを使わないといけないとかなんとか。。
例を見ると子コンポーネントで、navigatorを使っていて、それをindex.jsでimportする感じで。
今回のように、index.jsで直接使うときはどうするのかとか。
いろいろ難しそうなので、時間をかけて勉強してみます。

0Like

Your answer might help someone💌