برای اینکه بتوانیم از اکشنهای ناهمزمان مثل گرفتن اطلاعات از api استفاده کنیم، باید از redux-thunk استفاده کنیم.
قبل از شروع توصیه میکنم که سایر پستهای من در مورد ریداکس را مطالعه بفرمایید :
بیایید از فرمت اکشنها شروع کنیم. هر اکشن یک آبجکت مانند نمونه زیر است :
const action = { type: 'ADD_TO_LIST', payload:itemData, }
ویژگی type به نوع اکشن و payload هم دیتای مورد نیاز برای تغییر state است. ما برای reducer باید مستقیما یک object بفرستیم یا یک تابع action creator مانند زیر که یک object برمیگرداند :
به توابعی مانند تابع زیر که برای ما یک اکشن با فرمت مناسب تولید میکنند action creator میگوییم.
function addToList(itemData) { return { type:'ADD_TO_LIST', payload:itemData, } } store.dispatch(itemData);
اما مثلا وقتی که اکشن دریافت دیتا از سرور داریم :
function getFromAPI(){ result = loadData(); return { type:'LOAD_FROM_DATA', payload:result, } } store.dispatch(getFromAPI());
در این حالت dispatch به درستی عمل نخواهد کرد، چرا که به طور معمول ارسال اکشن به reducer بصورت همزمان یا سنکرون انجام میشود. در مثالهای بالاتر اکشن مورد نیاز reducer توسط یک تابع سنکرون ساخته میشود، dispatch آنرا فورا تحویل میگیرد و به reducer تحویل میدهد. اما در این مثال ما یک مشکل داریم : برای اینکه اکشن (یا همان object با فرمت اکشن) آماده شود، باید اطلاعات از سرور خوانده شوند، روی دیتای خوانده شده پردازش انجام شود و درنهایت تبدیل به فرمت مناسب برای اکشن شود، که این فرآیندی زمانبر است. پس ما نمیتوانیم از اکشن getFromAPI به طور معمول استفاده کنیم، زیرا یک action creator آسنکرون است.
برای حل این مشکل باید از middleware کمک بگیریم :
const middleware = store => next => action (){ const action = awaitForAction() next(action); }
درون middleware کاری که باید انجام بدهیم این است که کاری کنیم تا وقتی که اکشن نهایی همراه با دیتای موردنیاز آماده نشده، صبر کند و بعد از آماده شدن اکشن نهایی، آنرا به reducer تحویل دهد. بطور مثال وقتی میخواهیم دیتا از سرور بخوانیم ، باید برنامه صبر کند تا دیتا از سرور بیاید و وقتی دیتا تبدیل به فرمت اکشن مناسب شد، تابع next اجرا شود. کاری دردسرساز است، اما خبر خوب این است redux-thunk همین کار را برای ما میکند. به ما کمک میکند تا اکشنهای ناهمزمان یا اصطلاحا آسنکرون را بتوانیم dispatch کنیم.
خلاصه اگر میخواهیم با api درون redux کار کنیم یا باید خودمان یک middleware ایجاد کنیم یا از یک middleware آماده همانند redux-thunk استفاده کنیم.
ابتدا کتابخانه های redux و redux-thunk را نصب میکنیم :
npm i redux redux-thunk
برای استفاده باید یک store ایجاد کنیم :
import {createStore,applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; const initialState = { list:[], loading:true, error:false, } function reducer(state=initialState,action){ switch(action.type){ case GET_LIST: return {...state,loading:false,error:false,list:action.list}; case SET_LOADINRG: return {...state,loading:true}; case SET_ERROR: return {...state,loading:false,error:true} default: return state; } } const store = createStore( reducer, initialState, applyMiddleware(thunk), );
دقت کنید thunk را به عنوان middleware به store معرفی کردیم.
function setLoading() = { return { type: SET_LOADING, } } store.dispatch(setLoading() );
این اکشن سنکرون یا همزمان است. موقعی که dispatch میشود یک اکشن برمی گرداند و سپس آن اکشن مستقیما به reducer ارسال میشود و در نتیجه loading مقدار برابر با true میگیرد.
حالا به action creator زیر دقت کنید :
function getData() { return dispatch => { fetch(apiUrl).then(res=>res.json()).then(data=>{ dispatch({ type:GET_LIST, list:data.list, }); }).catch(err=>{ dispatch({ type:SET_ERROR, }); }); } } store.dispatch(getData());
تابع action creator ناهمزمان، برخلاف مدل همزمان یک تابع برای middleware ارسال میکند. این تابع در middleware اجرا میشود (کاری که برای انجام آن redux-thunk را نصب کردیم) درون این تابع وقتی نتیجه از سرور با موفقیت رسید، اکشن GET_LIST برای reducer ارسال میشود. اگر هم فرایند با خطا همراه بود، اکشن SET_ERROR به reducer فرستاده میشود.
امیدوارم پست مفید بوده باشه