
useStateを使用したloading表示
fetch APIなどを使って、以下のように指定されたURLに基づいて情報を取得したいことがあります。
App.tsx
import { useState } from 'react'
import './App.css'
type User = {
id: number
firstName: string
}
async function getUser(userId: string): Promise<User | null> {
try {
const response = await fetch(`https://dummyjson.com/users/${userId}`)
if (!response.ok) {
throw new Error('ユーザーが見つかりません')
}
const data: User = await response.json()
return data
} catch (error) {
console.error(error)
return null
}
}
function App() {
const [userId, setUserId] = useState<string>('1')
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(false)
const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
if (!userId) return
setUser(null)
setIsLoading(true)
const fetchedUser = await getUser(userId)
setIsLoading(false)
setUser(fetchedUser)
}
return (
<>
<form>
<input
type="number"
onChange={(e) => setUserId(e.target.value)}
value={userId}
/>
<button onClick={handleClick}>search</button>
</form>
{isLoading && <p>Loading...</p>}
{user && (
<div>
<p>id: {user.id}</p>
<p>firstName: {user.firstName}</p>
</div>
)}
</>
)
}
export default App
ReactのuseStateで「Loading…」を表示するサンプル
一見すると問題ないコードに見えますが、idを指定してリクエストして返ってくるデータが常に同じ場合もリクエストをして「Loading...」を表示しています。
このようなケースの場合は、一度取得したデータをローカルに保存しておいたほうがリクエストを減らして表示を高速化できます。
App.tsx
import React, { useState } from 'react'
import './App.css'
type User = {
id: number
firstName: string
}
async function getUser(userId: string): Promise<User | null> {
if (localStorage.getItem(userId)) {
const storageData = localStorage.getItem(userId)!
return JSON.parse(storageData)
}
try {
const response = await fetch(`https://dummyjson.com/users/${userId}`)
if (!response.ok) {
throw new Error('ユーザーが見つかりません')
}
const data: User = await response.json()
localStorage.setItem(String(data.id), JSON.stringify(data))
return data
} catch (error) {
console.error(error)
return null
}
}
function App() {
const [userId, setUserId] = useState<string>('1')
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState<boolean>(false)
const handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
if (!userId) return
setUser(null)
setIsLoading(true)
const fetchedUser = await getUser(userId)
setUser(fetchedUser)
setIsLoading(false)
}
return (
<>
<form>
<input
type="number"
onChange={(e) => setUserId(e.target.value)}
value={userId}
/>
<button onClick={handleClick}>search</button>
</form>
{isLoading && <p>Loading...</p>}
{user && (
<div>
<p>id: {user.id}</p>
<p>firstName: {user.firstName}</p>
</div>
)}
</>
)
}
export default App
1度リクエストしたデータはローカルに保存して、2度目はローカルのデータを返すサンプル
リクエストして返ってくるデータが常に同じ場合は、リクエストや「Loading...」の表示が無駄に発生しないよう、ローカルに保存して再利用すると良いでしょう。
ローカルのデータなら表示の待ち時間がほぼ0になるので、ユーザビリティも良くなります。