fetch APIで取得したデータはローカルに保存して再利用したほうが良い場合がある

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になるので、ユーザビリティも良くなります。