shinke1987.net
雑多な備忘録等のはず。
他のカテゴリ・タブ
目次
PR

React:useSWRの動作確認

2024-05-21 2025-03-30
カテゴリ: React

環境

React:v18.3.1

SWR:v2.2.5

インストール

下記コマンドを実行してインストールする。

1
% npm install swr

テスト用簡易サーバを用意

HTTPリクエストを受け取ってから2秒後にレスポンスを返す簡易サーバを用意する。

Expressのインストール

1
2
3
4
5
6
7
% mkdir フォルダ名 && cd フォルダ名
% npm init
% npm install express
 
// インストールされたExpressのバージョンは4.19.2だった。
 
% vim app.js

app.jsの内容

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const express = require('express')
const app = express()
const port = 3001
 
function sendResponse(res) {
  res.send('ApiServer : ' + Date());
}
 
const allowCrossDomain = function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
  res.header(
    'Access-Control-Allow-Headers',
    'Content-Type, Authorization, access_token'
  )
 
  // intercept OPTIONS method
  if ('OPTIONS' === req.method) {
    res.send(200)
  } else {
    next()
  }
}
 
app.use(allowCrossDomain)
 
app.get('/', (req, res) => {
  setTimeout(
    sendResponse,
    2000,
    res
  ); 
 
  // console.log('req = ', req);
  // console.log('res = ', res);
})
 
app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

上記app.jsをコピペしたら、下記コマンドを実行してサーバを起動する。

1
2
% nvm use node
% node app.js

サーバ起動後、Webブラウザで http://localhost:3001 へアクセスし、
「ApiServer : (日時)」と2秒後に表示されれば良い。

App.tsxの内容

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import React, {
  createContext, ReactNode, Suspense, useEffect, useLayoutEffect,
} from 'react';
import UseSwr from './UseSwr.tsx';
import Fetch from './Fetch.tsx';
 
  return (
    <>
      <Fetch />
      <hr />
      <Suspense fallback={<div>Now Loading...</div>}>
        <UseSwr />
      </Suspense>
    </>
  );
}
 
export default App;

Fetch.tsxの内容

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { ReactNode, useState } from 'react';
 
function Fetch(): ReactNode {
  const [showFetchData, setShowFetchData] = useState<Promise<string>|string>('初期値');
  const [startFetchData, setStartFetchData] = useState<string>('初期値');
  const [endFetchData, setEndFetchData] = useState<string>('初期値');
  const [countFetchData, setCountFetchData] = useState<number>(0);
 
  async function fetchData(): Promise<void> {
    setStartFetchData(Date());
 
    const response: Response = await fetch('http://localhost:3001');
    const promiseText: string = await response.text();
    setShowFetchData(promiseText);
 
    setEndFetchData(Date());
    setCountFetchData(countFetchData + 1);
  }
 
  return (
    <>
      <div>
        {`countFetchData = ${countFetchData}`}
        <br />
        {`startFetchData = ${startFetchData}`}
        <br />
        {`showFetchData = ${showFetchData}`}
        <br />
        {`endFetchData = ${endFetchData}`}
      </div>
 
      <div>
        <button
          type="button"
          onClick={fetchData}
        >
          fetchData
        </button>
      </div>
    </>
  );
}
 
export default Fetch;

UseSwr.tsxの内容

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
import { ReactNode, useState } from 'react';
import useSWR, { useSWRConfig } from 'swr';
 
function UseSwr(): ReactNode {
  // swrData関数用。
  const [startSwrData, setStartSwrData] = useState<string>('初期値');
  const [endSwrData, setEndSwrData] = useState<string>('初期値');
  const [countSwrData, setCountSwrData] = useState<number>(0);
  const [aboutCache, setAboutCache] = useState<ReactNode>(null);
 
  // キャッシュボタンのイベントハンドラで使用。
  const config = useSWRConfig();
 
  // useSWRフックで使用されるfetcher。
  async function fetcher(url: string): Promise<string> {
    setStartSwrData(Date());
 
    const promiseFetch: Response = await fetch(url);
 
    setEndSwrData(Date());
    setCountSwrData(countSwrData + 1);
 
    return promiseFetch.text();
  }
 
  // useSWRフックが成功した後に実行される関数。
  function swrOnSuccess(): void {
    console.log('swrOnSuccess');
    setCountSwrData(countSwrData + 1);
  }
 
  const { data, mutate } = useSWR<string>(
    fetcher,
    {
      suspense: true, // Suspenseコンポーネント用。20240521_非推奨となっている。
      onSuccess: swrOnSuccess, // 成功した後に実行される関数。
      revalidateIfStale: true, // 古いデータがあっても更新する。
      revalidateOnFocus: false, // フォーカスされても更新しない。
      revalidateOnReconnect: false, // ネットワーク接続復帰時に更新しない。
    },
  );
 
  const getAboutCache = () => {
    let component: ReactNode = '';
 
    config.cache.entries().forEach(([key, value]) => {
      component += `<div><div>key = ${key}</div><div>value.data = ${value.data}</div></div><br/>`;
    });
    component += `<div>config.cache.size = ${config.cache.size}</div>`;
 
    setAboutCache(component);
  };
 
  const handleRefresh = () => {
    // バウンドミューテートを利用。
    // Suspenseのfallbackは表示されない。
    mutate();
  };
 
  return (
    <>
      <div>
        {`countSwrData = ${countSwrData}`}
        <br />
        {`startSwrData = ${startSwrData}`}
        <br />
        {`showSwrData = ${data}`}
        <br />
        {`endSwrData = ${endSwrData}`}
      </div>
 
      <div>
        <button
          type="button"
          onClick={getAboutCache}
        >
          キャッシュ
        </button>
      </div>
 
      {aboutCache
        && (
          <>
            <br />
            <div dangerouslySetInnerHTML={{ __html: aboutCache }} />
          </>
        )}
 
      <br />
      <button
        type="button"
        onClick={handleRefresh}
      >
        更新
      </button>
    </>
  );
}
 
export default UseSwr;

結果

useSWRを利用したコンポーネントの作成例

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import { ReactNode } from 'react';
import useSWR from 'swr';
 
async function fetcher(tempUrl: string): Promise<string|null> {
  const response = await fetch(tempUrl);
  return response.text();
}
 
/**
 * useSWRを利用したコンポーネントを作成する場合の例。
 * @constructor
 */
 
function UseSwrComponent(): ReactNode {
  const url: string = 'http://localhost:3001';
 
 
  const {
    data, error, isLoading, isValidating, mutate,
  } = useSWR(
    url,
    fetcher,
  );
 
 
  if (error) return <div>error</div>;
  if (isLoading) return <div>loading</div>;
  if (isValidating) return <div>validating</div>;
 
 
  function handleBtnMutate() {
    mutate();
  }
 
 
  return (
    <div>
      <div>
        {data}
      </div>
      <div>
        <button
          type="button"
          onClick={handleBtnMutate}
        >
          mutate
        </button>
      </div>
    </div>
  );
}
 
export default UseSwrComponent;

コンポーネントの一部にuseSWRを利用したデータを表示する例(State不使用)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { ReactNode } from 'react';
import useSWR from 'swr';
 
async function fetcher(tempUrl: string): Promise<string|null> {
  const response = await fetch(tempUrl);
  return response.text();
}
 
/**
 * コンポーネントの一部分にuseSWRを利用したデータを表示する場合の例。
 * Stateを使わないやり方。
 * @constructor
 */
 
function ContainUseSwrData(): ReactNode {
  const url: string = 'http://localhost:3001';
  let contents: string = 'loading or validating';
 
 
  const {
    data, error, isLoading, isValidating, mutate,
  } = useSWR(
    url,
    fetcher,
  );
 
 
  // letで定義された変数でなく、useStateで作成されたset関数を利用するとTooManyReRenderのエラーとなる。
  if (error) contents = 'error';
  if (!isLoading && !isValidating) contents = String(data);
 
 
  function handleBtnMutate() {
    mutate();
  }
 
 
  return (
    <div>
      <div>
        {contents}
      </div>
      <div>
        <button
          type="button"
          onClick={handleBtnMutate}
        >
          mutate
        </button>
      </div>
    </div>
  );
}
 
export default ContainUseSwrData;

コンポーネントの一部にuseSWRを利用したデータを表示する例(State使用)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import { ReactNode, useEffect, useState } from 'react';
import useSWR from 'swr';
 
async function fetcher(tempUrl: string): Promise<string> {
  const response = await fetch(tempUrl);
  return response.text();
}
 
/**
 * コンポーネントの一部分にuseSWRを利用したデータを表示する場合の例。
 * Stateを利用するやり方。
 * @constructor
 */
function ContainUseSwrData2(): ReactNode {
  const url: string = 'http://localhost:3001';
  const [contents, setContents] = useState<string>('');
  let tempContents = 'loading or validating';
 
 
  const {
    data, error, isLoading, isValidating, mutate,
  } = useSWR(
    url,
    fetcher,
  );
 
 
  if (error) tempContents = 'error';
  if (!isLoading && !isValidating) tempContents = String(data);
 
 
  useEffect(() => {
    setContents(tempContents);
  }, [tempContents]);
 
 
  function handleBtnMutate() {
    mutate();
  }
 
 
  return (
    <div>
      <div>
        {contents}
      </div>
      <div>
        <button
          type="button"
          onClick={handleBtnMutate}
        >
          mutate
        </button>
      </div>
    </div>
  );
}
 
export default ContainUseSwrData2;
同一カテゴリの記事