環境
React:v18.3.1
SWR:v2.2.5
インストール
下記コマンドを実行してインストールする。
% npm install swr
テスト用簡易サーバを用意
HTTPリクエストを受け取ってから2秒後にレスポンスを返す簡易サーバを用意する。
Expressのインストール
% mkdir フォルダ名 && cd フォルダ名
% npm init
% npm install express
// インストールされたExpressのバージョンは4.19.2だった。
% vim app.js
app.jsの内容
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をコピペしたら、下記コマンドを実行してサーバを起動する。
% nvm use node
% node app.js
サーバ起動後、Webブラウザで http://localhost:3001 へアクセスし、
「ApiServer : (日時)」と2秒後に表示されれば良い。
App.tsxの内容
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の内容
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の内容
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>(
'http://localhost:3001',
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を利用したコンポーネントの作成例
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不使用)
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使用)
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;