واکشی (Fetch) کردن داده ها در اپلیکیشن های React – به زبان ساده


React به طور قابل درکی محبوبترین کتابخانه برای ساخت وب اپلیکیشنهای تعاملی است. با این حال React یک فریمورک وب صفر تا صد محسوب نمیشود. این فریمورک بر روی بخش View مدل MVC متمرکز شده است.
البته اکوسیستم کاملی از React وجود دارد که جنبههای دیگر را پوشش میدهد. در این راهنما در مورد یکی از اساسیترین اجزای وب اپلیکیشنها، یعنی نحوه دریافت (واکشی) دادهها برای نمایش آنها توضیح میدهیم. چند بخش در سلسله مراتب کامپوننتهای React وجود دارند که میتوان این وظیفه را انجام داد. زمان واکشی دادهها نیز مهم است. همچنین باید به فناوری مورد استفاده برای واکشی دادهها و این که کجا ذخیره میشوند نیز توجه داشت.
در انتهای این راهنما تصویر روشنی از طرز کار واکشی دادهها در React، معایب و محاسن رویکردهای متفاوت و چگونگی بهکارگیری این دانش در اپلیکیشنهای خودتان خواهید داشت.
سرآغاز
در ابتدا با استفاده از create-react-app یک چارچوب برای اپلیکیشن React خود میسازیم:
> create-react-app react-data-fetcher
نتیجه اجرای دستور فوق یک ساختار دایرکتوری کاملاً حساب شده است. اگر با create-react-app آشنا نیستید، این فایل راهنما را مطالعه کنید.
ایجاد یک سرور ساده
ما یک سرور ساده برای ذخیرهسازی و عرضه نقلقولها ایجاد کردیم. این جنبه مورد تمرکز این راهنما نیست و نقش آن صرفاً ارائه یک API ریموت برای نمایش طرز کار واکشی دادهها در React است. صرفاً برای رفع کنجکاوی شما بیان میکنیم که این سرور یک اپلیکیشن پایتون 3 روی فریمورک hug است که از Redis به عنوان پایگاه داده دائمی استفاده میکند.
بخش API کاملاً ساده است. یک نقطه انتهایی منفرد quotes/ وجود دارد. این نقطه در پاسخ به یک درخواست HTTP به صورت GET همه نقلقولهای ذخیره شده را باز میگرداند و شما میتوانید با ارسال درخواست POST نقلقولهایی را اضافه کنید. کد منبع به طور کامل روی این صفحه گیتهاب قرار دارد.
مروری بر اپلیکیشن دمو
اپلیکیشن دمو یک اپلیکیشن React است که با سرویس نقلقول تعامل دارد، همه نقلقولها را نمایش میدهد و اجازه میدهد نقلقول جدیدی اضافه کنید. تصویر زیر مربوط به این اپلیکیشن است.
ساختار اپلیکیشن بسیار ساده است. کار خود را با یک چارچوب ایجاد شده از سوی create-react-app آغاز کردهایم و سپس دو کامپوننت در زیر دایرکتوری src اضافه کردهایم: QuoteList و AddQuoteForm. در ادامه ساختار دایرکتوری (به جز node_modules) ارائه شده است.
~/git/react-data-fetcher > tree -I node_modules -L 2 . ├── README.md ├── README2.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── AddQuoteForm.css │ ├── AddQuoteForm.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── QuoteList.js │ ├── index.css │ ├── index.js │ └── registerServiceWorker.js └── yarn.lock 2 directories, 16 files
کد کامل منبع در این آدرس گیتهاب قرار دارد.
نمایش نقلقولها
کامپوننت کارکردی QuoteList فهرستی از همه نقلقولها را به صورت یک فهرست (bullet list) نمایش میدهد. این تابع یک آرایه از رشتهها میپذیرد:
import React from 'react' const QuoteList = ({quotes}) => quotes.map(quote => <li key={quote}>{quote}</li>) export default QuoteList
این کامپوننت فرزند کامپوننت main اپلیکیشن است.
واکشی دادهها با استفاده از Fetch API
API واکشی یک API مبتنی بر promise است که یک شیء response بازگشت میدهد. برای دریافت محتوای JSON واقعی باید متد ()json شیء response را فراخوانی کنید.
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) fetch(QUOTE_SERVICE_URL) .then(response => response.json()) .then(result => this.setState({quotes: result, isFetching: false})) .catch(e => console.log(e)); } }
نوشتن کد واکشی دادهها
وقتی از React صحبت میکنیم تمام سخن ما در مورد کامپوننتها است. بحث این که کد واکشی دادهها را باید کجا قرار دهیم، بسیار مهم است. اگر کد خود را به خوبی بنویسید کامپوننتهای ژنریک زیاد و برخی کامپوننتهای خاص اپلیکیشن خواهید داشت. React و جاوا اسکریپت به طور کلی بسیار انعطافپذیر هستند و از این رو امکان تزریق منطق به همه بخشهای آن وجود دارد.
واکشی نقلقولها از یک REST API نیازمند نوعی poll کردن است و از این رو نقلقولها همواره باید بهروز باشند؛ اما واکشی اولیه نیز مهم است. کامپوننتهای React متدهای دارای چرخه عمر دارند که میتوانید برای پیادهسازی منطق استفاده کنید. این کامپوننتها در زمان خاصی فعال میشوند. متد ()componentDidMount زمانی فعال میشود که کامپوننت بتواند دسترسی یابد و حالت آن تغییر یابد. این نقطهای عالی برای مقداردهی اولیه فرایند واکشی دادهها محسوب میشود. این کار به صورت زیر صورت میگیرد:
componentDidMount() { this.fetchQuotes() }
اگر واقعاً میخواهید این کد را در نخستین نمایش قرار دهید باید از ()componentWillMount برای مقداردهی اولیه واکشی ناهمگام استفاده کنید؛ اما این ریسک وجود دارد که فرایند واکشی دادهها پیش از نصب کامپوننت کامل شود. بنابراین این رویکرد توصیه نمیشود.
انتخاب زمانهایی که واکشی دادهها صورت میگیرد
واکشی اولیه در ()componentDidMount عالی است؛ اما ما میخواهیم که نقلقولها مرتباً بهروز شوند. در API مبتنی بر REST تنها راهحل این کار آن است که به طور دورهای از سرور poll کنیم. سرویس نقلقول بسیار ابتدایی است و همواره همه نقلقولها را باز میگرداند.
سرویسهایی با مقیاسپذیری بیشتر روشی برای بررسی بهروز شدن یا حتی استفاده از if-modify-since در HTTP و یا حتی eTag را ارائه میدهند. اپلیکیشن دموی ما تنها کاری که انجام میدهد این است که هر چند ثانیه یک بار با یک تایمر که در ()componentDidMount آغاز و در ()componentWillUnmount متوقف میشود، همه نقلقولها را واکشی میکند:
componentDidMount() { this.fetchQuotes() this.timer = setInterval(() => this.fetchQuotes(), 5000); } componentWillUnmount() { this.timer = null; }
مدت poll یک تصمیم وابسته به اپلیکیشن است. اگر میخواهید بهروزرسانیهای همزمان داشته باشید، این کار باعث میشود که فشار بیشتری روی بکاند وارد شود و از این رو بهتر است از WebSockets به جای REST استفاده کنید.
مدیریت واکشیهای دادهای که مدتی طولانی اجرا شدهاند
برخی اوقات واکشی دادهها ممکن است مدت زیادی طول بکشد در این موارد نمایش یک نوار پیشروی یا یک انیمیشن جذاب به کاربر اطلاع میدهد که اپلیکیشن مشغول کار است و کمک زیادی به ارتقای تجربه کاربری اپلیکیشن میکند. این مسئله به طور خاص هنگامی که کاربر واکشی دادهها را برای اولین بار از طریق زدن دکمه جستجو آغاز میکند، حائز اهمیت است.
در اپلیکیشن دمو این کار به سادگی با نمایش یک پیام به صورت «...Fetching quotes» هنگام اجرا عملیات واکشی نمایش مییابد. در متد ()render کامپوننت main اپلیکیشن با بررسی عضو state.isFetching از یک رندرینگ شرطی استفاده شده است.
render() { const title = 'Quotes for ya!' let now = new Date() return ( <div className='App'> <h2 className='App-title'>{title}</h2> <p>{this.state.isFetching ? 'Fetching quotes...' : ''}</p> <QuoteList quotes={this.state.quotes} /> <AddQuoteForm quote_service_url={QUOTE_SERVICE_URL}/> </div> ); }
متد ()fetchQuotes به مدیریت آپلود کردن state.isFetching میپردازد. این کار از طریق مقداردهی اولیه آن به صورت true هنگام آغاز به کار و بازگشت به false هنگام دریافت نقلقولها صورت میگیرد.
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) fetch(QUOTE_SERVICE_URL) .then(response => response.json()) .then(result => this.setState({quotes: result, isFetching: false})) .catch(e => console.log(e)); } }
مدیریت خطاها
در این اپلیکیشن کار زیادی در خصوص مدیریت خطا صورت نگرفته است و صرفاً خطاهایی که رخ میدهند در کنسول گزارش میشوند. بسته به این که اپلیکیشن شما چگونه است، میتوانید از برخی منطقهای تلاش مجدد، اطلاعرسانی به کاربر یا نمایش محتوای خطای بازگشتی استفاده کنید.
API واکشی در برابر Axios
API واکشی چند مشکل دارد. یکی این که نیازمند یک مرحله بیشتر برای استخراج JSON از پاسخ است. همچنین همه خطاها را ثبت نمیکند. برای نمونه خطای 404 به عنوان یک پاسخ نرمال بازگشت مییابد. شما باید کد response را بررسی کنید و همچنین خطاهای شبکه را که ثبت میشوند، بررسی کنید.
بنابراین باید در دو مکان با خطاها برخورد کنید؛ اما میتوانید از کتابخانه axios.js استفاده کنید که این مشکلات را رفع کرده و با افزودن وابستگیهای خارجی کد را اندکی فشردهتر ساخته است. در ادامه کدی که با axios نوشته شده است را میبینید:
fetchQuotes = () => { this.setState({...this.state, isFetching: true}) axios.get(QUOTE_SERVICE_URL) .then(response => this.setState({quotes: response.data, isFetching: false})) .catch(e => console.log(e); }
این تغییر چندان بزرگی محسوب نمیشود؛ اما به هر حال کمککننده است. کد افزودن نقلقول با axios بسیار فشردهتر است. در ادامه نسخه fetch این کد را میبینید:
handleSubmitWithFetch = event => { let data = new FormData() data.append('quote', this.state.quote) fetch(this.props.quote_service_url, {method: 'POST', body: data}) .then(response => response.json()) .catch(e => console.log(e)); event.preventDefault(); }
و این نسخه axios کد افزودن نقلقول است:
handleSubmit = event => { axios.post(this.props.quote_service_url, {'quote': this.state.quote}) .then(r => console.log(r)) .catch(e => console.log(e)); event.preventDefault(); }
سخن پایانی
در این راهنما شیوه واکشی دادهها به صورت ناهمگام در یک اپلیکیشن React را آموختید. ما به بررسی متدهای چرخه عمر مربوطه، poll کردن از سرور، گزارش پیشرفت کار و مدیریت خطا پرداختیم.
همچنین در این نوشته به بررسی دو کتابخانه مبتنی بر promise یعنی fetch API و axios.js پرداختیم. اینک شما میتوانید دست به کار شوید و اپلیکیشنهای جذاب React بسازید که از API ریموت بهره میگیرند. در طی سالیان اخیر React محبوبیت فزایندهای کسب کرده است. امیدواریم از مطالعه این نوشته لذت برده باشید.
اگر این مطلب برای شما کاربردی بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- آموزش فریمورک React — ساخت یک سیستم طراحی با قابلیت استفاده مجدد
- مجموعه آموزشهای برنامهنویسی
- ری اکت (React) — راهنمای جامع برای شروع به کار
- ۱۰ کتابخانه و فریمورک جاوا اسکریپت که باید آنها را بشناسید
- مجموعه آموزشهای طراحی و برنامه نویسی وب
==
با سلام و عرض ادب ای کاش به جای استقاذه از کلماتی مانند “نقل و قول” و کلمات مشابه از کلمات تخصصی و native این حوزه استفاده میکردید تا مفاهیم گمراه کننده نبود.