در درس قبلی بخش مشکل یکپارچه کردن برنامه ری اکت با Redux را انجام دادیم. در این درس، یک سری قابلیت برای تنظیمات Redux تعریف می کنیم.
تا به اینجا برنامه ای که ایجاد کردیم می تواند به خوبی زمان جاری را نشان می دهد. اما هیچ راهی برای بروزرسانی یک زمان جدید وجود ندارد، که در این درس می خواهیم این مشکل را حل کنیم.
به خاطر داشته باشید که تنها با استفاده از یک action creator می توانیم داده ها را در Redux تغییر دهیم. در درس قبلی یک redux store را ایجاد کردیم، اما هیچ راه حلی برای بروزرسانی این store را ایجاد نکردیم.
کاری که می خواهیم انجام دهیم این است که به کاربران این امکان را بدهیم تا با کلیک روی دکمه زمان را بروزرسانی کنند. برای اضافه کردن این قابلیت باید مراحل زیر را طی کنید:
ما قبلا این سه مرحله را پیاده سازی کرده بودیم، اما دو کار دیگر است که باید انجام دهیم تا برنامه مطابق خواسته ما اجرا شود.
در درس قبلی، ما گفتیم که اکشن ها چه چیزی هستند، اما نگفتیم که چرا باید از actionCreator ها استفاده کنیم، و یا اصلاً اینها چه چیزی هستند.
به بیان ساده، اکشن یک آبجکت ساده است که باید یک مقدار type داشته باشد. قبلاً یک فایل به نام types.js ایجاد کردیم که ثابت های action type یا نوع اکشن ها را نگهداری می کرد، بنابراین می توانیم از این مقادیر به عنوان پروپرتی type استفاده کنیم.
export const FETCH_NEW_TIME = 'FETCH_NEW_TIME';
همان طور که گفتیم، اکشن ها می توانند هر نوع آبجکتی که شامل یک کلید type هستند، باشند، همچنین می توانیم داده ها را از طریق این اکشن ها ارسال کنیم (به طور قراردادی، داده های اضافی توسط پروپرتی payload یک اکشن ارسال می کنیم)
{ type: types.FETCH_NEW_TIME, payload: new Date().toString() }
حال باید این اکشن را به Store نرم افزار ارسال (dispatch) کنیم:
store.dispatch({ type: types.FETCH_NEW_TIME, payload: new Date().toString() })
اما این کار یک راه حل خوب نیست. بجای اینکه یک اکشن را به طور مستقیم ارسال کنیم، بهتر است از یک تابع که یک اکشن را بر می گرداند، به جای اینکار استفاده نماییم، یعنی تابعی که یک اکشن را ایجاد خواهد کرد (بخاطر همین به آن actionCreator می گویند).
این روش از نظر تست پذیری، قابلیت استفاده مجدد، مستندسازی و کپسوله سازی منطق برنامه، نسبت به روش قبلی برتری دارد.
حال اولین actionCreator
خود را در فایل redux/actionCreator.js
ایجاد می کنیم.
در این فایل یک تابع که مسئولیت برگرداندن یک اکشن برای ارسال به store است را تعریف و سپس export می کنیم.
import * as types from './types'; export const fetchNewTime = () => ({ type: types.FETCH_NEW_TIME, payload: new Date().toString(), }) export const login = (user) => ({ type: types.LOGIN, payload: user }) export const logout = () => ({ type: types.LOGOUT, })
حال اگر این تابع را فراخوانی کنیم، یک آبجکت را به ما بر می گرداند. اما چطور باید این اکشن را گرفته و به Store ارسال کنیم.
اگر به یاد داشته باشید، در درس قبلی از تابع ()connect
که از پکیج react-redux، export شده بود، استفاده کردیم. اولین آرگومان آن mapStateToProp
بود که state را به یک آبجکت prop نگاشت (تبدیل) می کرد. اما این تابع یک آرگومان دوم هم می گیرد که به ما اجازه می دهد تا به راحتی توابع را به یک props، نگاشت کنیم.
حال اجازه بدهید به صورت عملی این قابلیت را به شما نشان دهیم. در فایل src/views/Home/Home.js
با ارائه یک تابع دیگر برای استفاده از actionCreator
که قبلاً ایجاد کرده ایم، فراخوانی متد ()connect
را بروزرسانی می کنیم.
در پارامتر دوم تابع connect
، متد mapDispatchToProps
را فراخوانی می کنیم.
import { fetchNewTime } from '../../redux/actionCreators'; // ... const mapDispatchToProps = dispatch => ({ updateTime: () => dispatch(fetchNewTime()) }) // ... export default connect( mapStateToProps, mapDispatchToProps, )(Home);
حال، تابع ()updateTime
به عنوان یک props به آن پاس داده شد و هنگامی که یک action اتفاق بیفتد، متد ()dispatch
فراخوانی می شود. حال کامپوننت <Home/>
را بروزرسانی می کنیم و یک دکمه در آن قرار می دهیم تا کاربر با فشار دادن این دکمه، زمان را بروزرسانی کند.
const Home = (props) => { return ( <div className="home"> <h1>Welcome home!</h1> <p>Current time: {props.currentTime}</p> <button onClick={props.updateTime}> Update time </button> </div> ); }
اگر چه این مثال خیلی جذاب به نظر نمی رسد، اما به خوبی ویژگی و قابلیت های Redux را نشان می دهد. به طور مثال می توانیم یک دکمه داشته باشیم و با فشار دادن آن یک توئیت جدید از سرور واکشی شود یا از یک اتصال سوکت برای بروزرسانی redux store استفاده کنیم. همین مثال ساده، قابلیت های کامل Redux را نشان می دهد.
تا به اینجا ما فقط از یک reducer
برای برنامه خود استفاده کردیم. تا الان چون ما به حجم کمی از داده های ساده کار می کردیم و تنها یک نفر روی این برنامه کار می کرد، این روش جواب می داد. اما فرض کنید که با حجم بزرگی از داده ها کار می کردید، آن وقت چطور؟
Redux این مشکل را برای شما حل می کند. Redux قابلیتی دارد که توسط آن می توانید reducerهای برنامه تان را به چندین reducer مجزا تقسیم کنید که هر کدام از این Reducerها مسئولیت یک بخش از ساختار stateها را بر عهده داشته باشد.
ما می توانیم از تابع ()combineReducers
که در پکیج redux قرار گرفته برای نوشتن یک آبجکتی از توابع reducer استفاده کنیم.
سپس بعد از این که اکشنی اتفاق افتاد، هر کدام از این توابع reducer متناسب با اکشن اتفاق افتاده، اجرا می شود. حال می خواهیم بصورت عملی این قابلیت را بررسی کنیم.
فرض کنید که می خواهیم وضعیت کاربر فعلی را بررسی کنیم. برای این منظور یک ماژول Redux به نام currentUser
در مسیر src/redux/currentUser.js
ایجاد می کنیم.
touch src/redux/currentUser.js
حال ما همان چهار مقداری که از ماژول currentTime
استخراج کرده بودیم را در اینجا export می کنیم، اما در اینجا به طور مشخص برای کاربر فعلی استفاده می کنیم.
پس یک ساختار ساده برای مدیریت یک کاربر اضافه می کنیم:
import * as types from './types' export const initialState = { user: {}, loggedIn: false } export const reducer = (state = initialState, action) => { switch (action.type) { case types.LOGIN: return { ...state, user: action.payload, loggedIn: true}; case types.LOGOUT: return { ...state, user: {}, loggedIn: false}; default: return state; } }
حال تابع ()configureStore
را برای در نظر گرفتن این تقسیم بندی ها و با استفاده از combineReducer
ها دو دسته بندی را از آن جدا می کنیم.
import { createStore, combineReducers } from 'redux'; import { rootReducer, initialState } from './reducers' import { reducer, initialState as userInitialState } from './currentUser' export const configureStore = () => { const store = createStore( combineReducers({ time: rootReducer, user: reducer }), // root reducer { time: initialState, user: userInitialState }, // our initialState ); return store; } export default configureStore;
حال می توانیم actionCreatorهای ()login
و ()logout
را برای ارسال اکشن ها به store ایجاد کنیم.
export const login = (user) => ({ type: types.LOGIN, payload: user }) // ... export const logout = () => ({ type: types.LOGOUT, })
حال ما می توانیم از actionCreatorها برای فراخوانی login و logout همانند تابع ()updateTime
استفاده کنیم.
در این درس، چرخه بین بروزرسانی و ذخیره داده ها در Redux state سراسری را کامل کردیم. به علاوه چطور Redux را توسعه داده تا بتوانیم از چندین Reducer و اکشن به همراه چندین کامپوننت متصل شده، استفاده کنیم.
در درس بعدی یاد می گیریم که چطور از middlewareها برای مدیریت واکشی داده های خارجی و قرار دادن آنها در برنامه استفاده کنیم، تا به این طریق قدرت Redux برای نگهداری از داده ها را به شما نشان دهیم.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.