یکی از متداولترین مسئولیتهای توسعهدهندگان فرانتاند، مدیریت دادهها در برنامههای فرانتاندشان است. باید بتوانیم دادهها را از یک API بازیابی کنیم، بر روی آنها تغییراتی ایجاد نماییم و سپس در یک برنامه وب مدرن برای تعامل با کاربر، روی صفحه نمایش دهیم.
وجود یک ارتباط کارآمد بین قسمت فرانتاند و بکاند برای ایجاد برنامههای یکپارچه و ریسپانسیو بسیار مهم است.
اکنون سناریویی را تصور میکنیم که در آن با یک توسعهدهنده بکاند بر روی یک پروژه کار میکنیم و منتظر API endpoint هستیم تا به فرانتاند خود متصل شویم. یک ابزار عالی وجود دارد که توسعهدهندگان فرانتاند میتوانند از آن برای ایجاد یک API ساختگی در مرحله توسعه استفاده کنند. این ابزار JSON Server نام دارد.
در این مقاله قصد داریم تا نحوه استفاده از JSON serverها برای ذخیرهسازی داده در برنامههای React را بررسی کنیم. همچنین با پیادهسازی یک پروژه فرانتاند، با ویژگیها و مزایای آن بیشتر آشنا خواهیم شد.
برنامهای که قصد داریم آن را پیادهسازی کنیم به کاربران این امکان را میدهد تا لیستی از کاربران و جزئیات اطلاعات آنها را مشاهده کنند. دادههای کاربر با استفاده از JSON server در یک فایل JSON در برنامه فرانتاند ایجاد میشود.
JSON مخفف عبارت JavaScript Object Notation است و یک ابزار Node.js سبک و با کاربری آسان میباشد که یک API RESTful را با استفاده از یک فایل JSON به عنوان data source شبیهسازی میکند. توسعهدهندگان فرانتاند با کمک JSON Server میتوانند APIهای ساختگی را بدون نیاز به نوشتن کدهای پیچیده سمت سرور یا زمانی که API پشتیبان هنوز آماده نیست ایجاد کنند.
این API ساختگی درخواستها را به endpointای که ارائه خواهد شد ارسال میکند، به درخواستهای HTTP پاسخ میدهد و به این ترتیب کار را برای توسعه سریع برای توسعهدهندگان فرانتاند ایدهآل میکند. JSON Server همچنین توسعهدهندگان را قادر میسازد تا عملیات CRUD را انجام دهند و دادهها را در فایلهای JSON ذخیره کنند. فایل JSON به عنوان مقادیر key-value و در قالب زیر نوشته میشود:
{
"name": "Jane",
"age": 30,
"gender": "Female"
}
به name، age و gender keyگفته میشود و Jane، 30 و female value هر یک از این keyها هستند.
فایل داده JSON میتواند در دو فرمت آرایه و آبجکت با آیجکتهای تودرتو ارائه شود.
فرمت آرایه
[
{
"id": 1,
"name": "John Doe",
"age": 25
},
{
"id": 2,
"name": "Jane Smith",
"age": 40
}
]
فرمت آبجکت با آبجکتهای تودرتو
{
"users": {
"1": {
"name": "John Doe",
"age": 25
},
"2": {
"name": "Jane Smith",
"age": 30
}
}
}
در ادامه برخی از ویژگیهای JSON Server را داریم که عبارتند از:
GET، POST، PUT و DELETE پشتیبانی میکند.Create، Read، Update و Delete (CRUD) را روی دادهها انجام دهیم تا بتوانیم یک برنامه تعاملی بسازیم.در این بخش به برخی از مزایای استفاده از JSON Server اشاره میکنیم که عبارتند از:
ما باید Node.js و npm را روی سیستم خود نصب کنیم، زیرا هر دو پیش نیاز این تنظیمات هستند. سپس مراحل زیر را برای راهاندازی و استفاده از JSON Server در برنامه فرانتاند خود دنبال میکنیم:
برای نصب JSON Server در برنامه خود، به دایرکتوری پروژه خود در ترمینال یا خط فرمان میرویم و دستور زیر را تایپ میکنید:
npm install -g json-server
با این کار JSON Server به صورت سراسری روی سیستم ما نصب میشود. اگر بخواهیم آن را به صورت محلی فقط برای یک پروژه خاص نصب کنیم، از دستور زیر استفاده میکنیم:
npm i json-server
یک فایل JSON در دایرکتوری پروژه خود ایجاد میکنید که به عنوان data source عمل میکند. این فایل JSON باید پسوند فایل json.داشته باشد. یعنی اگر بخواهیم نام فایل JSON ما dbباشد، فایلی به نام db.jsonایجاد میکنیم.
دادههای خود را در فایل JSON تعریف میکنیم. این دادههای JSON میتوانند آرایهای از آبجکتها یا یک آبجکت با آبجکتها تودرتو باشند. هر آبجکت یک موجودیت داده را نشان میدهد که باید یک id منحصربهفرد داشته باشد.
JSON Server را با تایپ این دستور در ترمینال خود راهاندازی میکنید: json-server --watch db.json. به طور پیشفرض روی آدرس https://localhost:3000اجرا میشود. هنگام راهاندازی سرور با استفاده از flag --port، میتوانیم پورتی را که روی آن اجرا میشود، با تعیین شماره پورت دیگری تغییر دهیم.
به عنوان مثال، اگر بخواهیم سرور ما به جای پورت پیشفرض (3000) روی پورت 8000 اجرا شود، هنگام راهاندازی سرور باید از این دستور استفاده کنیم: json-server --watch db.json --port 8000. سپس میتوانیم آن را در مرورگر خود روی پورت 8000 مشاهده نماییم.
JSON Server به طور خودکار endpointهای RESTful را بر اساس دادههایی که در فایل JSON خود تعریف کردهایم، ایجاد میکند.
اگر یک فایل JSON با آرایهای از users داریم، این endpoint است که به طور خودکار توسط JSON Server ایجاد میشود:
GET /users– فهرستی از تمام موجودیتهای منابع کاربران را بازیابی میکند.GET /users/:id– یک کاربر خاص را با id خود بازیابی میکند.POST /users– یک کاربر جدید ایجاد میکند.PUT /users/:id– یک کاربر را بر اساس id مشخص شده بهروزرسانی میکند.DELETE /users/:id– یک کاربر را بر اساس id مشخص شده حذف میکند.این الگو تعامل با API ساختگی را به روش RESTful و درست مانند API واقعی بکاند انجام میدهد.
برای درک بیشتر نحوه استفاده از JSON Server در یک پروژه واقعی، یک مثال را باهم بررسی میکنیم. ما یک برنامه ساده React.js خواهیم ساخت که دادههای کاربران را از یک فایل داده JSON با استفاده از JSON Server در قسمت فرانتاند نمایش میدهد.
این برنامه فقط دو صفحه خواهد داشت. صفحه اول لیستی از کاربران همراه با نام و آدرس ایمیل آنها را دربرمیگیرد. با کلیک روی دکمه مشاهده جزئیات کامل، وارد صفحه دیگری میشویم که جزئیات بیشتر در مورد هر کاربر را بر اساس id کاربری آن، نمایش میدهد.
با استفاده Vite یک برنامه React ایجاد میکنیم. برای انجام این کار دستور زیر را اجرا مینماییم:
yarn create vite
نام پروژه را انتخاب کرده و React را به عنوان فریمورک و تایپ اسکریپت را به عنوان variant انتخاب میکنیم. در صورت تمایل میتوانیم از جاوااسکریپت هم به عنوان variant استفاده کنیم.
پس از انجام این کار، به پروژه خود میرویم و با کمک دستور yarn نصب dependencyها را انجام میدهیم. پس از اینکه dependencyها را با موفقیت نصب کردیم، سرور توسعه خود را با این دستور اجرا میکنیم: yarn run dev. مرورگر خود را باز کرده و URL (http://127.0.0.1:5173/) را جایگذاری مینماییم و به این ترتیب، برنامه در حال اجرا خود را در مرورگر مشاهده میکنیم.
با استفاده از دستور زیر JSON server را در برنامه خود نصب میکنید:
برای نصب سراسری: npm install -g json-server
برای نصب محلی: npm i json-server
آخرین پکیجی که نصب میکنیم react-router است تا با استفاده از آن، از page navigation بهرهمند شویم: npm i react-router-dom.
اکنون میخواهیم برنامهای که داریم ساختار پوشهای مرتب داشته باشد، بنابراین این دستورالعملها را دنبال میکنیم:
ابتدا یک پوشه data در دایرکتوری root پروژه خود میسازیم. در داخل این پوشه data، یک فایل JSON به نام db.json ایجاد میکنیم. این فایل جایی است که دادههای JSON تعریف میشوند.
سپس، در دایرکتوری src، یک پوشه components ایجاد میکنیم. در این پوشه 3 فایل Home.tsx، UserDetails.tsx و UserList.tsx را میسازیم. اینها کامپوننتهایی هستند که منطق و رابط کاربری برنامه را رندر میکنند.
در دایرکتوری src یک فایل با نام useFetch.tsx ایجاد میکنیم. این فایل حاوی کد اجرای API خواهد بود. کامپوننت اصلی برنامه ما، یعنی فایل App.tsx جایی است که ما مسیریابی صفحات را در آن مدیریت خواهیم کرد.
اولین کامپوننتی که باید تغییراتی را در آن ایجاد کنیم فایل App.tsx است. خطوط کد زیر را در کامپوننت قرار میدهیم:
import Home from './components/Home';
import UserDetails from './components/UserDetails';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users/:id" element={<UserDetails />} />
</Routes>
</Router>
)
}
export default App
این فایل فقط برای رندر مسیرهایی برای برنامه است.
به فایل db.json میرویم و دادههای JSON خود را در داخل آن تعریف میکنیم:
{
"users": [
{
"id": 1,
"name": "Juliet Oma",
"email": "julie@yahoo.com",
"number": "08100000000"
},
{
"id": 2,
"name": "James Williams",
"email": "jameswilly@gmail.com",
"number": "08111111111"
},
{
"id": 3,
"name": "Ahmed Ali",
"email": "ahmedali012@gmail.com",
"number": "09022222222"
},
{
"id": 4,
"name": "Grace Funsho",
"email": "gracefunsho@gmail.com",
"number": "09033333333"
}
]
}
server خود را با استفاده از دستور زیر راهاندازی میکنیم:
json-server --watch db.json --port 8000
این دستور فایل db.json را مشاهده میکند و API endpointای را که روی پورت 8000 اجرا میشود، درون آن قرار میدهد.
اگر URL (http://localhost:8000/users) ارائه شده در ترمینال را کپی کنیم و آن را در مرورگر خود باز نماییم، یک داده JSON خواهیم دید که تمام دادههای کاربری را که در فایل db.json خود تعریف کردهایم به نمایش میگذارد.
در مرحله بعد، باید برای پیادهسازی API کدنویسی انجام دهیم. این کار را در فایل useFetch.tsx انجام خواهیم داد. این فایل اساساً یک هوک سفارشی React است که برای مدیریت دریافت دادههای asynchronous از یک URL مشخص، ایجاد میکنیم.
import { useState, useEffect } from 'react';
interface UseFetchResult {
data: any | null;
isPending: boolean;
error: any | null;
}
const useFetch = (url: string): UseFetchResult => {
const [data, setData] = useState<any | null>(null);
const [isPending, setIsPending] = useState<boolean>(true);
const [error, setError] = useState<any | null>(null);
useEffect(() => {
setTimeout(() => {
fetch(url)
.then(res => {
if (!res.ok) {
throw Error('Error fetching users data');
}
return res.json();
})
.then(data => {
setData(data);
setIsPending(false);
setError(null);
})
.catch(err => {
setIsPending(false);
setError(err.message);
});
}, 1000);
}, [url]);
return { data, isPending, error };
}
export default useFetch;
ابتدا هوکهای useState و useEffect را import میکنیم تا از آنها برای مدیریت stateها و انجام side effectها در هوک سفارشی استفاده کنیم.
هنگام تنظیم پروژه، تایپ اسکریپت را به عنوان یک variant انتخاب کردیم. بنابراین، باید یک interface به نام useFetchResult تعریف کنیم تا ساختار آبجکت نتیجهای را که هوک useFetch return میکند را مشخص نماید. این interface شامل سه ویژگی است: data، isPending و error.
کد const useFetch = (url: string): UseFetchResult => { ... } هوک سفارشی useFetch را تعریف میکند. یک پارامتر url از نوع رشته میگیرد و یک آبجکت برمیگرداند که با اینترفیس UseFetchResult مطابقت دارد.
در مرحله بعد، باید با استفاده از هوک useState سه متغیر را مقداردهی اولیه کنیم: data برای ذخیره دادههای دریافت شده، isPending برای نشان دادن اینکه آیا دریافت داده در حال انجام است یا خیر و state error برای ذخیره هر گونه خطایی که در طول فرآیند دریافت رخ میدهد. هر کدام به ترتیب دارای مقدار اولیه null، true و null هستند.
هوک useEffect برای دریافت دادهها هنگام تغییر URL استفاده میشود. این هوک پس از رندر اولیه و هر زمان که وابستگی URL تغییر کند، اجرا میشود. در داخل تابع useEffect، یک setTimeOut برای شبیهسازی یک تاخیر 1000 میلی ثانیهای (1 ثانیه) قبل از شروع دریافت دادهها تعریف شده است.
از متد fetch برای درخواست GET به URl مشخص شده استفاده میشود. responseای که برمیگردد با استفاده از res.ok بررسی میشود. اگر ok نباشد، خطا رخ میدهد. سپس response با استفاده از متد res.json() به JSON تبدیل شده و در متغیر data ذخیره میشود. state isPending روی false تنظیم میشود تا نشان دهد دریافت دادهها کامل شده است و state error روی null تنظیم میشود تا خطای قبلی پاک شود.
اگر در طول فرآیند دریافت دادهها خطایی رخ دهد، در بلاک .catch ثبت میشود. isPending روی false تنظیم میشود و سپس state error با پیام خطا بهروزرسانی میگردد.
هوک سفارشی یک آبجکت حاوی data، isPending و stateهای error را return میکند و به کامپوننتهای دیگر اجازه میدهد تا به داده دریافت شده و state آن دسترسی داشته باشند.
UserList.tsx کامپوننتی است که تمام اطلاعات کاربر را در یک جدول نمایش میدهد. در فایل UserList.tsx خود، کد زیرا را مینویسیم:
import React from 'react';
import { Link } from 'react-router-dom';
interface User {
id: number;
name: string;
email: string;
number: string;
}
interface UserListProps {
users: User[];
name: string;
}
const UserList: React.FC<UserListProps> = ({ users, name }) => {
return (
<>
<section>
<section>
<h1>Users List</h1>
</section>
<section>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<tr key={user.id}>
<td>
<p>{user.name}</p>
</td>
<td>
<p>{user.email}</p>
</td>
<td>
<Link to={`/users/${user.id}`}>
<button>
View full details
</button>
</Link>
</td>
</tr>
))}
</tbody>
</table>
</section>
</section>
</>
);
};
export default UserList;
کار خود را با import کردن Link از کتابخانه react-router-dom شروع میکنیم.
خط بعدی کد interface User است، یک interface تایپ اسکریپ که ساختار یک آبجکت کاربر را تعریف میکند که دارای چهار ویژگی است و هر ویژگی دارای یک نوع داده مشخص میباشد.
سپس interface UserListProps را داریم که ساختار یک آبجکت را تعریف میکند که دارای دو ویژگی user و name است. users آرایهای از آبجکتها است که با interface User مطابقت دارد.
این کامپوننت یک آبجکت با interface userListProps را به عنوان آرگومان میگیرد، users را از آن destruct کرده و به عنوان props در کامپوننت استفاده میکند.
پس از آن، المنت JSX را برای نمایش لیست کاربران در قالب جدول داریم. ما با استفاده از متد map بر روی users هر کاربر را در یک ردیف رندر میکنیم.
سپس یک دکمه به هر ردیف اضافه میکنیم که عمل navigation را به یک مسیر خاص (/users/${user.id}) انجام میدهد. این مسیرها اطلاعات دقیق کاربری را که id او به عنوان بخشی از URL ارائه شده است، نمایش میدهد.
ما از یک قالب literal برای ایجاد یک URL داینامیک که شامل مقدار user.id است استفاده کردیم.
کامپوننت بعدی که روی آن کار خواهیم کرد، کامپوننت Home.tsx است. این همان جایی است که لیست کاربران بر روی صفحه، نمایش داده میشود و صفحه اصلی برنامه ما به حساب میآید.
import UserList from './UserList';
import useFetch from '../useFetch';
const Home = (): JSX.Element => {
const { data: users, isPending, error } = useFetch('http://localhost:8000/users')
return (
<section>
{error && <p>{error}</p>}
{isPending && <p>Loading users...</p>}
{users && <UserList users={users} />}
</section>
);
};
export default Home;
ابتدا کامپوننت UserList و هوک سفارشی useFetch را از جایی که در دایرکتوری پروژه قرار دارند import میکنیم.
خط کد const { data: users, isPending, error } = useFetch('http://localhost:8000/users') هوک سفارشی useFetch را برای دریافت دادهها از URL مشخص شده فراخوانی میکند (http://localhost:8000/users). هوک یک آبجکت با سه ویژگی return میکند: data(کاربران دریافت شده)، isPending(وضعیت لود) و error(پیام خطا).
ما رابط کاربری را با المنتهای JSX رندر کردیم. اگر state error دارای یک مقدار باشد، یک المنت پاراگراف حاوی پیام خطا را رندر میکند. اگر state isPending مقدار true داشته باشد، یک المنت پاراگراف را نمایش میدهد که نشان میدهد users در حال بارگیری است. درنهایت اگر state users دادهای داشته باشد (یعنی null نباشد)، کامپوننت UserList را رندر میکند و دادههای کاربران را به عنوان prop ارسال مینماید.
ما میخواهیم یک functionality اضافی به برنامه خود بیافزاییم که در آن میتوانید بر روی دکمه مشاهده جزئیات کامل که در همان ردیف با نام کاربر قرار دارد کلیک کنیم و جزئیات بیشتری را در مورد آن کاربر خاص در صفحه دیگری مشاهده نماییم. در کامپوننت Userdetails.tsx کد زیر را داریم:
import { useParams } from 'react-router-dom';
import useFetch from '../useFetch';
const UserDetails = () => {
const { id } = useParams();
const { data: user, error, isPending } = useFetch("http://localhost:8000/users/" + id);
return (
<>
<section>
{isPending && <p>Loading user details...</p>}
{error && <p>{error}</p>}
{user && (
<>
<h1>User {user.id} details</h1>
<h2>{user.name}</h2>
<p>{user.email}</p>
<p>{user.number}</p>
</>
)}
</section>
</>
);
};
export default UserDetails;
هوک useParams را از کتابخانه react-router-dom import میکنیم. این هوک امکان دسترسی به پارامترهای URL را برای ما فراهم میکند. همچنین هوک سفارشی useFetch را هم import میکنیم.
const { id } = useParams(); از هوک useParams برای استخراج پارامتر id از URL استفاده میکند. این کد معمولاً در مسیرهایی مانند /users/:id مورد استفاده قرار میگیرد.
در خط بعدی، هوک سفارشی useFetch را برای دریافت دادهها از URL یک کاربر خاص بر اساس پارامتر id استخراج شده، فراخوانی میکند. هوک یک آبجکت با ویژگی های data، error و isPending را return میکند.
سپس المنتهای JSX میآیند تا جزئیات کاربران را رندر کنند. {isPending && <p>Loading user details...</p>} یک پاراگراف رندر میکند که نشان میدهد در صورتی که isPending مقدار true داشته باشد، جزئیات کاربر بارگیری میشود.
اگر خطایی وجود داشته باشد، {error && <p>{error}</p>} یک پاراگراف را نمایش میدهد که پیام خطا را در آن به نمایش میگذارد. در حالی که {user && (...)} برخی از جزئیات کاربر مانند id، name، email و number را در صورت موجود بودن دادهها رندر میکند.
هنگامی که به مرورگر خود بازگردیم، لیستی از دادههای کاربر را در یک جدول مشاهده خواهیم کرد. هر ردیف کاربر در جدول دارای دکمهای است که میتوانیم روی آن کلیک کنیم تا صفحهای برای ما باز شود و جزئیات کامل آن کاربر خاص را ببینیم.
پس از استایلدهی برنامه، یک دمو از آن را در این لینک داریم.
همینطور میتوانیم دادههای کاربر را تغییر دهیم یا جزئیات بیشتری مانند توضیحات شغل کاربران، عنوان آنها و غیره را در فایل db.json خود به لیست کاربران خود اضافه کنیم و تغییرات را در فهرست مرورگر مشاهده نماییم.
در این مقاله سعی کردیم تا با JSON server و نحوه استفاده از آن در یک برنامه React.js فرانتاند آشنا شویم.
همچنین میتوانیم عملیات CRUD کامل را با این دادهها از فایل JSON خود در قسمت فرانتاند برنامهای که داریم انجام دهیم. ما در این مقاله فقط عملیات Read را بررسی کردیم.
همینطور مثالی که باهم بررسی کردیم، درخواستی را به API endpoint ارائه شده هنگام راهاندازی JSON server ارسال میکند و پاسخ را در مرورگر نمایش میدهد. این اساساً نحوه عملکرد یک API server بکاند واقعی است. در این مثال، ما توانستیم با استفاده از JSON server به فانکشنالیتی مورد نظر خود دست پیدا کنیم.
مهم است بدانیم که این API ساختگی JSON را نمیتوانیم در مرحله تولید مورد استفاده قرار دهیم. فقط در مرحله توسعه برای ایجاد دادههای ساختگی JSON قابل استفاده میباشد. این بدان معناست که ما نمیتوانیم آن را به پروژه نهایی ارسال کنیم زیرا فایل داده JSON فقط بر روی یک پورت localhost اجرا میشود.