در این پست، میخواهیم نحوه ارسال درخواستهای HTTP از React را بررسی کنیم. ما میتوانیم request ها را از هر component ارسال کنیم، اما ما می خواهیم این کار را با متمرکز کردن HTTP logic به عنوان یک repository انجام دهیم. برای پروژه های با اندازه کوچک، همچین راه حل متمرکزی کاملاً مناسب است.
اما اگر پروژه بزرگتر و پیچیدهتری دارید، جدا کردن فایلهای repository و در نتیجه جداسازی state ها، راهکار مناسب تری میباشد. ما قصد داریم از axios به عنوان یک کتابخانه third party برای ارسال درخواست های HTTP و از Redux برای متمرکز سازی repository logic استفاده کنیم.
نکته مهمی وجود دارد که باید به آن توجه کرد. ما مجبور نیستیم از Redux در React برای ایجاد یک مکان متمرکز برای handle کردن درخواستهای HTTP استفاده کنیم (این فقط یک شیوه است)، بلکه همچنین میتوانیم یک فایل اضافی ایجاد کرده و function ها (که درخواستهای HTTP را handle میکنند) را از آن فایل export کنیم. اما Redux در اپلیکیشنهای React کاملا مرسوم است. بنابراین ما قصد داریم نحوه کارکرد Redux را با جزییات شرح دهیم.
این مقاله، قسمتی از مجموعه آموزشی زیر میباشد:
- آماده سازی پروژه و ایجاد component ها
- Navigation و مسیریابی (Routing)
- Axios ،HTTP و Redux
- Lazy Loading و HOC Component
- مدیریت خطا و component های اضافی
- ایجاد form داینامیک و component های modal
- اعتبارسنجی Form و Handle کردن درخواست Post
- مدیریت درخواست PUT
- مدیریت درخواست DELETE
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی NET Core. را ببینید، به این لینک مراجعه کنید: دستورالعملهای کار با NET Core.
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی React را ببینید، به این لینک مراجعه کنید: مقدمه معرفی آموزش سریالی React
برای بررسی قسمت قبل، به این لینک مراجعه کنید: Navigation و مسیریابی.
جهت دانلود سورس بر روی این لینک کلیک کنید: سری آموزشی React – قسمت 3.
این پست به قسمتهای زیر تقسیم میشود:
- ایجاد Axios Instance
- Redux در اپلیکیشن React
- Action Type – Redux ها
- Repository Actions (Action Container) – Redux
- Reducer – React
- ثبت فایل Reducer
- نتیجه گیری
ایجاد Axios Instance
برای نصب axios، این دستور را اجرا کنید:
1 |
npm install --save axios |
حتی اگرچه میتوانیم از نمونه axios native (همینی که به تازگی نصب کردهایم) برای ارسال درخواستهای HTTP استفاده کنیم، راه بهتر این است که instance خودمان را ایجاد کنیم. در این instance سفارشی، میتوانیم ویژگی base url را تعریف کرده و headers و سایر عناصر مفید را به آن اختصاص دهیم. اگر به نمونه های بیشتری از axios نیاز داشته باشیم، می توانیم آنها را نیز ایجاد کنیم. بنابراین اجازه دهید تا نمونه axios سفارشی خود را ایجاد کنیم.
داخل پوشه src
، یک پوشه جدید ایجاد میکنیم و نام آن را axios میگذاریم. داخل این پوشه، یک فایل جدید ایجاد کرده و نام آن را axios.js بگذارید. حالا فایل axios.js را به این صورت اصلاح میکنیم:
1 2 3 4 5 6 7 8 9 10 |
import axios from 'axios'; const instance = axios.create({ baseURL: 'http://localhost:5000', headers: { headerType: 'example header type' } }); export default instance; |
در این کد، ما کتابخانه axios را import کرده ایم و سپس یک instance جدید با یک سری ویژگیها درون آن ایجاد میکنیم. این خیلی مفید است، به این دلیل که دیگر مجبور نیستیم که endpoint ها را به صورت کامل برای درخواستهای HTTP بنویسیم (
http://locahost:5000/api/owneraxios.get(
)
). حالا میتوانیم از مسیرهای نسبی (axios.get(/api/owner)
) استفاده کنیم، به این دلیل که ما داریم از نمونه سفارشی axios به همراه ویژگی baseURL از پیش تعریف شده استفاده میکنیم.
Redux در اپلیکیشن React
Redux یک state container برای اپلیکیشنهای جاوااسکریپت است. اگرچه، این یک کمی در ابتدا پیچیده به نظر میرسد، اما با کمی تمرین، متوجه خواهید شد که Redux اصلا آنقدرها هم سخت نیست و کمک زیادی نیز به ما خواهد کرد. Redux یک کتابخانه React نیست. بلکه میتواند با هر فریم ورک جاوااسکریپتی تطابق پیدا کند. اما با React خوب کار میکند.
برای نصب Redux، این دستور را اجرا کنید:
1 |
npm install --save redux |
Redux خودش به تنهایی کافی نیست. ما باید یک رابطه بین React و Redux برقرار کنیم و برای انجام آن، باید کتابخانه react-redux را نصب کنیم:
1 |
npm install --save react-redux |
عالی.
حالا ما برای کار با Redux در اپلیکیشن خود، هر دو کتابخانه مورد نیاز را داریم. بنابراین، میتوانیم کار خود را با پیاده سازی Redux ادامه دهیم. اما قبل از ادامه، بیایید به این نمودار نگاهی بیندازیم که به طور دقیق نحوه عملکرد Redux را توضیح می دهد (قسمت Components یک نقطه شروع است):
Action Type – Redux ها
اجازه دهید با ایجاد ساختار زیر داخل پوشه src شروع کنیم:
در پوشه actions
، یک فایل جدید ایجاد کنیم و نام آن را actionTypes.js بگذاریم. حالا این فایل را اصلاح کنیم:
1 2 3 4 |
export const GET_DATA_SUCCESS = 'GET_DATA_SUCCESS'; export const POST_DATA_SUCCESS = 'POST_DATA_SUCCESS'; export const PUT_DATA_SUCCESS = 'PUT_DATA_SUCCESS'; export const DELETE_DATA_SUCCESS = 'DELETE_DATA_SUCCESS'; |
فایل reducer ما (که بعدا در همین پست ایجاد خواهیم کرد) از این action type ها برای switch کردن بین شیوه های مختلف آپدیت کردن state استفاده میکند.
Repository Actions (Action Container) – Redux
ما باید یک فایل جدید در پوشه actions
ایجاد کنیم و نام آن را repositoryActions.js بگذاریم. ما قصد داریم در خواستهای HTTP async را داخل این فایل handle کنیم و یک object که فایل reducer از آن برای آپدیت کردن state استفاده میکند را برگردانیم. هر object برگشتی باید حداقل یک ویژگی با نام “type” داشته باشد. مقدار این ویژگی type باید یکی از actionType های موجود در فایل actionType.js (که به تازگی ایجاد شده است) باشد.
حالا پس در فایل repositoryActions.js، دستورات import را برای actionTypes و axios instance خود اضافه میکنیم:
1 2 |
import * as actionTypes from './actionTypes'; import axios from '../../axios/axios'; |
سپس باید دو function اضافه کنیم. یکی برای handle کردن GET request و دیگری برای return کردن یک object با ویژگی type و data از server:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const getDataSuccess = (data) => { return { type: actionTypes.GET_DATA_SUCCESS, data: data } } export const getData = (url, props) => { return (dispatch) => { axios.get(url) .then(response => { dispatch(getDataSuccess(response.data)); }) .catch(error => { //TODO: handle the error when implemented }) } } |
چه کاری داریم اینجا انجام میدهیم؟
ما داریم تابع getData را اینجا export میکنیم. این تابع از component ما برای fetch کردن داده ها از سرور فراخوانی خواهد شد (بنابراین باید آن را از این فایل export کنیم). سپس با axios، درخواست GET را درون این تابع میفرستیم. اگر درخواست موفقیت آمیز باشد، در این صورت تابع getDataSuccess
را dispatch میکنیم که این تابع، یک object برای فایل reducer جهت استفاده برمیگرداند. این object، ویژگی الزامی type و همچنین ویژگی data واکشی شده از سرور را در اختیار دارد.
در زیر تابع getData، بیایید همه توابع دیگر را با پیروی از همین الگوی مشابه پیاده سازی کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
const postDataSuccess = (response) => { return { type: actionTypes.POST_DATA_SUCCESS, response: response } } export const postData = (url, obj, props) => { return (dispatch) => { axios.post(url, obj) .then(response => { dispatch(postDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } } const putDataSuccess = (response) => { return { type: actionTypes.PUT_DATA_SUCCESS, response: response } } export const putData = (url, obj, props) => { return (dispatch) => { axios.put(url, obj) .then(response => { dispatch(putDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } } const deleteDataSuccess = (response) => { return { type: actionTypes.DELETE_DATA_SUCCESS, response: response } } export const deleteData = (url, props) => { return (dispatch) => { axios.delete(url) .then(response => { dispatch(deleteDataSuccess(response)); }) .catch(error => { //TODO: handle the error when implemented }) } } |
تمام. حالا ما فایل repositoryActions.js را به همراه پیاده سازی در اختیار داریم و حالا وقت ایجاد و پیاده سازی فایل reducer میباشد.
Reducer – React
یک فایل جدید داخل پوشه reducers ایجاد کرده و نام آن را repositoryReducer.js بگذارید:
در این فایل، ما قصد داریم ویژگی type که از فایل repositoryActions.js برمیگردانیم را بررسی کنیم. سپس بسته به مقدار ویژگی state ،type خود را آپدیت میکنیم.
پس فایل repositoryReducer.js را اصلاح میکنیم:
1 2 3 4 5 6 |
import * as actionTypes from '../actions/actionTypes'; const initialState = { data: null, showSuccessModal: false } |
ما actionTypes را اینجا import میکنیم و state را با نام initialState ایجاد میکنیم. ویژگی data، داده ها را از server ذخیره میکند و ویژگی showSuccessModal
برای نمایش یا مخفی کردن success modal زمانیکه اکشن POST ،PUT یا DELETE با موفقیت اجرا میشود به کار برده میشود.
حالا یک تابع reducer زیر آبجکت state خود ایجاد میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
const reducer = (state = initialState, action) => { switch (action.type) { case actionTypes.GET_DATA_SUCCESS: return executeGetDataSuccess(state, action); case actionTypes.POST_DATA_SUCCESS: return executePostDataSuccess(state, action); case actionTypes.PUT_DATA_SUCCESS: return executePutDataSuccess(state, action); case actionTypes.DELETE_DATA_SUCCESS: return executeDeleteDataSuccess(state, action); default: return state; } } export default reducer; |
این تابع reducer، دو پارامتر قبول میکند. پارامتر اول، state است که ما آن را با initial state خود مقداردهی اولیه کرده ایم و پارامتر دوم، action است. ما قصد داریم از این پارامتر state برای آپدیت کردن initialState
خود و از پارامتر action برای ذخیره object (با حداقل ویژگی type) ارسال شده از فایل repositoryActions.js استفاده کنیم. بنابراین هر زمان که ما هر action (که یک object با حداقل ویژگی type و تمام ویژگیهای دیگر) را از فایل dispatch ،repositoryActions.js کنیم، این تابع reducer، آبجکت ارسال شده به داخل پارامتر action را trigger و قبول میکند. در نتیجه، reducer از طریق action type ها، switch کرده و تابع مربوطه را اجرا میکند.
در آخر، این تابع های مربوطه را درست بالای تابع reducer
خود اضافه میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
const executeGetDataSuccess = (state, action) => { return { ...state, data: action.data } } const executePostDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } } const executePutDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } } const executeDeleteDataSuccess = (state, action) => { return { ...state, showSuccessModal: true } } |
این تابع ها، state ما را آپدیت میکنند. ابتدا، ما با استفاده از عملگر spread (…) از آبجکت state خود، یک کپی عمیق میگیریم و سپس فقط ویژگی که می خواهیم در آبجکت state خود به روز رسانی کنیم را override می کنیم. از آنجایی که object ها و آرایه ها، reference type هستند، باید قبل از هر گونه تغییر، یک clone عمیق روی آنها اجرا کنیم. به این ترتیب ما state را به طور تغییرناپذیر به روز می کنیم.
برای اینکه این state داخل هر owner component در دسترس باشد باید این reducer را به داخل فایل index.js ثبت کنیم.
ثبت فایل Reducer
قبل از ثبت فایل reducer خود، باید یک کتابخانه third-party دیگر به نام thunk را نصب کنیم:
1 |
npm install --save redux-thunk |
این کتابخانه باعث میشود تا بتوانیم درخواستهای async را با اکشنهای Redux بفرستیم.
حالا میتوانیم reducer خود را ثبت کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './containers/App'; import registerServiceWorker from './registerServiceWorker'; import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap/dist/css/bootstrap-theme.css'; import repositoryReducer from './store/reducers/repositoryReducer'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; const store = createStore(repositoryReducer, applyMiddleware(thunk)); ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root')); registerServiceWorker(); |
در این کد بالا، تمام فایلهایی که برای ثبت reducer خود به آنها نیاز داریم را import کرده ایم. سپس ما store را ایجاد کرده ایم و middleware thunk را اعمال کرده ایم و در نهایت از کامپوننت Provider
جهت در اختیار قرار دادن reducer خود در اپلییکشن React استفاده کرده ایم.
اینجا، ما تنظیمات redux خود را برای پروژه خود آماده کرده ایم. بهترین قسمت آن این است که ما می توانیم آن را برای هر component در داخل پروژه خود استفاده کنیم و اگر component هایی داریم که به تنظیمات پیچیده تر یا متفاوتی نیاز دارند، تنها کاری که باید انجام دهیم این است که یک فایل action و reducer دیگر ایجاد کرده و آن را درون فایل index.js ثبت کنیم. در یکی از پستهای بعدی، ما به شما نشان می دهیم که چگونه می توانید این کار را با ترکیب reducer ها در داخل فایل index.js انجام دهید.
نتیجه گیری
اگرچه ممکن است درک نحوه عملکرد Redux برای شما کمی سخت باشد، اما ما معتقدیم که با این مقاله و مقداری تمرین، خواهید توانست آن را handle کرد. نکته آخر این است که Redux آنقدرها هم سخت نیست، درست برعکس، پس از مدتی تمرین بسیار ساده نیز خواهد بود.
با خواندن این مقاله، شما یاد گرفتید:
- شیوه آماده سازی Axios instance جدید
- نحوه نصب Redux و React-Redux
- در مورد Action Type ها، Action Container ها و Reducer ها
- نحوه ثبت فایل reducer
بابت خواندن این مقاله از شما تشکر میکنیم و امیدواریم که برای شما مفید واقع قرار گرفته باشد.
در قسمت بعدی این سری از آموزش، قصد داریم Lazy Loading و HOC Component را یاد بگیریم.