ブログに戻る

ReactとリアルタイムAPIで通貨コンバーターを構築する方法

V
Vlado Grigirov
April 07, 2026
"Currency API" "React" "Tutorial" "JavaScript" "Exchange Rates" "Finexly"

ReactとリアルタイムAPIで通貨コンバーターを構築する方法

ReactでAPI統合、React Hooksを練習したい開発者にとって、通貨コンバーターの構築は最適なプロジェクトです。このステップバイステップチュートリアルでは、170以上の通貨をサポートし、リアルタイムで為替レートを取得する完全に機能する通貨コンバーターを構築します。すべてモダンなReactパターンを使用して実装します。

為替レートの仕組みについて初めて学ぶ方は、為替レートの解説をご参照ください。

構築するもの

このチュートリアルが終わると、以下の機能を持つReact通貨コンバーターが完成します:

  • Finexly APIからリアルタイムの為替レートを取得
  • ドロップダウンで170以上の通貨をサポート
  • ユーザーが入力するたびにリアルタイムで変換(デバウンス付き)
  • ローディング状態とAPIエラーを優雅に処理
  • レートと最終更新タイムスタンプを表示

プロジェクト構成:

currency-converter/
├── src/
│   ├── components/
│   │   └── CurrencyConverter.tsx
│   ├── hooks/
│   │   └── useExchangeRate.ts
│   ├── App.tsx
│   └── main.tsx
├── package.json
└── vite.config.ts

前提条件

開始前に以下を確認してください:

  • Node.js 18+がインストール済み
  • React HooksuseStateuseEffect)の基本的な知識
  • 無料のFinexly APIキー(取得に約30秒)

ステップ1:Reactプロジェクトのセットアップ

Viteをビルドツールとして使用します:

npm create vite@latest currency-converter -- --template react-ts
cd currency-converter
npm install
npm run dev

開発サーバーがhttp://localhost:5173で起動します。

ステップ2:Finexly APIを理解する

Finexly APIは、シンプルなREST インターフェースを通じて170以上の通貨のリアルタイムおよび過去の為替レートを提供します:

GET https://finexly.com/api/v1/latest?base=USD

典型的なレスポンス:

{
  "base": "USD",
  "date": "2026-04-07",
  "rates": {
    "EUR": 0.9182,
    "GBP": 0.7871,
    "JPY": 151.42
  },
  "timestamp": 1744012800
}

リクエストヘッダーにキーを含めます:

Authorization: Bearer あなたのAPIキー

無料プランに登録すると、月1,000リクエストが含まれます。

ステップ3:カスタムHookの作成

src/hooks/useExchangeRate.tsを作成します:

import { useState, useEffect, useCallback } from "react";

interface ExchangeRateResult {
  rates: Record<string, number> | null;
  loading: boolean;
  error: string | null;
  lastUpdated: Date | null;
}

const API_KEY = import.meta.env.VITE_FINEXLY_API_KEY;
const BASE_URL = "https://finexly.com/api/v1";

const rateCache: Record<string, { rates: Record<string, number>; timestamp: number }> = {};
const CACHE_TTL_MS = 5 * 60 * 1000;

export function useExchangeRate(baseCurrency: string): ExchangeRateResult {
  const [rates, setRates] = useState<Record<string, number> | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);
  const [lastUpdated, setLastUpdated] = useState<Date | null>(null);

  const fetchRates = useCallback(async (base: string) => {
    const cached = rateCache[base];
    if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
      setRates(cached.rates);
      setLastUpdated(new Date(cached.timestamp));
      return;
    }

    setLoading(true);
    setError(null);

    try {
      const res = await fetch(`${BASE_URL}/latest?base=${base}`, {
        headers: { Authorization: `Bearer ${API_KEY}` },
      });

      if (!res.ok) throw new Error(`APIエラー: ${res.status} ${res.statusText}`);

      const data = await res.json();
      rateCache[base] = { rates: data.rates, timestamp: Date.now() };
      setRates(data.rates);
      setLastUpdated(new Date());
    } catch (err) {
      setError(err instanceof Error ? err.message : "為替レートの取得に失敗しました");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    if (baseCurrency) fetchRates(baseCurrency);
  }, [baseCurrency, fetchRates]);

  return { rates, loading, error, lastUpdated };
}

ステップ4:CurrencyConverterコンポーネントの構築

src/components/CurrencyConverter.tsxを作成します:

import { useState, useMemo } from "react";
import { useExchangeRate } from "../hooks/useExchangeRate";

const CURRENCIES = [
  "USD", "EUR", "GBP", "JPY", "CAD", "AUD", "CHF", "CNY",
  "INR", "MXN", "BRL", "KRW", "SGD", "HKD", "NOK", "SEK",
];

export default function CurrencyConverter() {
  const [amount, setAmount] = useState<string>("100");
  const [baseCurrency, setBaseCurrency] = useState<string>("USD");
  const [targetCurrency, setTargetCurrency] = useState<string>("EUR");

  const { rates, loading, error, lastUpdated } = useExchangeRate(baseCurrency);

  const convertedAmount = useMemo(() => {
    if (!rates || !amount) return null;
    const numAmount = parseFloat(amount);
    if (isNaN(numAmount) || numAmount < 0) return null;
    const rate = rates[targetCurrency];
    return rate ? (numAmount * rate).toFixed(2) : null;
  }, [rates, amount, targetCurrency]);

  const handleSwap = () => {
    setBaseCurrency(targetCurrency);
    setTargetCurrency(baseCurrency);
  };

  return (
    <div className="converter-card">
      <h2>リアルタイム通貨コンバーター</h2>

      <div className="input-row">
        <label htmlFor="amount">金額</label>
        <input
          id="amount"
          type="number"
          min="0"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="金額を入力"
        />
      </div>

      <div className="currency-row">
        <div className="select-group">
          <label htmlFor="base">変換元</label>
          <select id="base" value={baseCurrency} onChange={(e) => setBaseCurrency(e.target.value)}>
            {CURRENCIES.map((c) => <option key={c} value={c}>{c}</option>)}
          </select>
        </div>

        <button className="swap-btn" onClick={handleSwap} aria-label="通貨を入れ替え">⇄</button>

        <div className="select-group">
          <label htmlFor="target">変換先</label>
          <select id="target" value={targetCurrency} onChange={(e) => setTargetCurrency(e.target.value)}>
            {CURRENCIES.map((c) => <option key={c} value={c}>{c}</option>)}
          </select>
        </div>
      </div>

      <div className="result-area">
        {loading && <p>最新レートを取得中...</p>}
        {error && <p className="error-text">⚠ {error}</p>}
        {!loading && !error && convertedAmount !== null && (
          <>
            <p className="converted-value">
              {parseFloat(amount).toLocaleString()} {baseCurrency} =&nbsp;
              <strong>{parseFloat(convertedAmount).toLocaleString()} {targetCurrency}</strong>
            </p>
            {lastUpdated && <p>レート更新: {lastUpdated.toLocaleTimeString()}</p>}
          </>
        )}
      </div>
    </div>
  );
}

ステップ5:環境変数

プロジェクトルートに.envファイルを作成します:

VITE_FINEXLY_API_KEY=あなたのAPIキー

APIキーを保護するために.env.gitignoreに追加してください。

ステップ6:アプリを接続する

src/App.tsxを更新します:

import CurrencyConverter from "./components/CurrencyConverter";

function App() {
  return (
    <main>
      <CurrencyConverter />
      <footer>
        <p>為替レート提供: <a href="https://finexly.com">Finexly</a></p>
      </footer>
    </main>
  );
}

export default App;

さらに進むために

基本的なコンバーターが動作したら、高価値な機能拡張として、Finexly過去データAPIを使った30日間レートチャート、同時多通貨変換、localStorageへの通貨ペア保存、Next.jsによるサーバーサイドレンダリングなどが挙げられます。

よくある質問

ReactにはどのAPIが最適ですか? CORS対応のREST APIが必要です。Finexly APIはすべてのプランでCORSをサポートし、無料枠で170以上の通貨をカバーしています。

為替レートはどのくらいの頻度で更新されますか? Finexly APIはリアルタイムプランでは60秒ごと、スタンダードプランでは1日1回更新されます。

クライアントサイドのReactアプリで通貨APIを使えますか? APIがCORSをサポートしていれば使えます。本番アプリでは、自分のバックエンドを経由してAPIリクエストを行うことをお勧めします。

ReactでAPIキーを露出させないようにするには? 開発時はViteの.envファイルでVITE_プレフィックスを使用します。本番では、Next.js API RouteやServerless関数を通じてリクエストをルーティングしてください。

USD以外の2通貨間で変換するには? Finexly APIはすべてのプランで任意のベース通貨をサポートしているため、これが大幅に簡単になります。


ReactアプリにリアルタイムAPIを追加する準備ができましたか?無料のFinexly APIキーを取得 — クレジットカード不要。月1,000回の無料リクエストから始め、成長に合わせてアップグレードしてください。

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →

この記事を共有する