كيفية بناء محول العملات باستخدام React وواجهة برمجة أسعار الصرف الفوري
يُعدّ بناء محول العملات باستخدام React مشروعاً ممتازاً للمطورين الذين يرغبون في ممارسة تكامل واجهات برمجة التطبيقات (API)، وهوك React، وإدارة الحالة في التطبيقات الفعلية. في هذا الدليل التعليمي خطوة بخطوة، ستقوم ببناء محول عملات فعّال بالكامل يجلب أسعار صرف فورية من واجهة برمجة تطبيقات العملات، ويدعم أكثر من 170 عملة، ويتعامل مع الأخطاء بأناقة — كل ذلك باستخدام أنماط React الحديثة.
إذا كنت مبتدئاً وتريد فهم كيفية عمل أسعار الصرف، راجع شرح أسعار الصرف قبل البدء.
ما ستبنيه
بنهاية هذا الدليل، سيكون لديك محول عملات React يمكنه:
- جلب أسعار الصرف الفورية من Finexly API
- دعم أكثر من 170 عملة عبر قائمة منسدلة
- تحويل المبالغ فورياً أثناء كتابة المستخدم (مع debouncing)
- التعامل بأناقة مع حالات التحميل وأخطاء API
- عرض السعر والطابع الزمني لآخر تحديث
هيكل المشروع:
currency-converter/
├── src/
│ ├── components/
│ │ └── CurrencyConverter.tsx
│ ├── hooks/
│ │ └── useExchangeRate.ts
│ ├── App.tsx
│ └── main.tsx
├── package.json
└── vite.config.tsالمتطلبات المسبقة
قبل البدء، تأكد من توفر:
- Node.js 18+ مُثبَّت
- معرفة أساسية بـ React Hooks (
useState،useEffect) - مفتاح API مجاني من Finexly (يستغرق الأمر حوالي 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 أسعار صرف فورية وتاريخية لأكثر من 170 عملة عبر واجهة REST بسيطة:
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" dir="rtl">
<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} =
<strong>{parseFloat(convertedAmount).toLocaleString()} {targetCurrency}</strong>
</p>
{lastUpdated && <p>تحديث الأسعار: {lastUpdated.toLocaleTimeString()}</p>}
</>
)}
</div>
</div>
);
}الخطوة 5: متغيرات البيئة
أنشئ ملف .env في جذر المشروع:
VITE_FINEXLY_API_KEY=مفتاح_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، أو التحويل المتزامن لعملات متعددة، أو حفظ أزواج العملات المفضلة في localStorage، أو التصيير من جانب الخادم مع Next.js.
الأسئلة الشائعة
ما هو أفضل API لأسعار الصرف لـ React؟ تحتاج تطبيقات React إلى REST API يدعم CORS. تدعم Finexly API CORS في جميع الخطط وتغطي أكثر من 170 عملة مع مستوى مجاني.
كم مرة تُحدَّث أسعار الصرف؟ تُحدِّث Finexly API الأسعار كل 60 ثانية للخطط الفورية ومرة يومياً للخطط القياسية.
هل يمكنني استخدام API العملات في تطبيق React من جانب العميل؟ نعم، إذا كانت API تدعم CORS. بالنسبة لتطبيقات الإنتاج، يُفضل توجيه الطلبات عبر خادمك الخاص.
كيف أتجنب الكشف عن مفتاح API في React؟
للتطوير، استخدم ملف .env الخاص بـ Vite مع البادئة VITE_. للإنتاج، وجّه الطلبات عبر Next.js API Route أو دالة serverless.
كيف أحول بين عملتين غير USD؟ تدعم Finexly API أي عملة أساسية في جميع الخطط، مما يبسط هذا الأمر بشكل كبير.
هل أنت مستعد لإضافة أسعار صرف فورية إلى تطبيق React الخاص بك؟ احصل على مفتاح Finexly API المجاني — لا حاجة لبطاقة ائتمانية. ابدأ بـ 1,000 طلب مجاني شهرياً وقم بالترقية مع نموك.
Explore More
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 →