
useStateを使用したloading表示
Reactでfetch APIなどを使って、指定されたURLに基づいて情報を取得する非同期処理を行うことがあります。
その場合、データを取得するまで時間がかかるため、以下のようにデータを取得するまでuseStateで状態を管理して「Loading...」のようなを表示させることが多いです。
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…」を表示するサンプル
useTransitionを使用したloading表示
useTransitionとはUIをブロックすることなく状態を更新できるReact Hookです。
前述のコードではuseStateを利用して、setIsLoadingにboolean値を設定して「Loading...」の表示・非表示を切り替えていました。
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)
}
useTransitionを使用すればsetIsLoadingでtrue, falseを設定せずにisLoadingの状態を変更できるので、コードがシンプルになります。
setIsLoading(true), setIsLoading(false)だと記述する場所を間違えて正しく動作しないケースがあるので、useTransitionを使用したほうがバグも発生しづらいです。
import React, { useState, useTransition } 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, startTransition] = useTransition()
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
if (!userId) return
setUser(null)
startTransition(async () => {
const fetchedUser = await getUser(userId)
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のuseTransitionで「Loading…」を表示するサンプル
※ 2024年9月現在、React v19を使用するには「npm i react@next react-dom@next」でインストールが必要です。
※ useTransitionで非同期関数が使えるのはv19以降なので、動作しなければ{React.version}でバージョンが19以上か確認してください。
※ 2024年9月現在は「Argument of type '() => Promise' is not assignable to parameter of type 'TransitionFunction'.」の警告が出てしまいます。
※ useTransitionを使用する方法だと、データをlocalStorageで保存して、すでに取得したデータの場合はローカルに保存したデータを返す処理を使用している場合は、「Loading...」が一瞬表示されてしまう問題が発生します。useStateだとこの問題は発生しない。