ریداکس (Redux) — مبانی مقدماتی
اصطلاحهای تخصصی زیادی در دنیای ریداکس وجود دارند. اکشن، اکشنساز، انواع اکشن، کاهنده، کاهندههای ریشه و مواردی از این دست. در پس همه این مجموعه واژگان پیچیده، کدهای سادهای وجود دارند که موجب بهبود زیادی در روش سازماندهی و نوشتن اپلیکیشنهای ریاکت شدهاند. در این مطلب تلاش خواهیم کرد برخی از این مفاهیم مقدماتی و مبانی ریداکس را توضیح دهیم. همچنین قصد داریم نگاهی به پکیجهای پراستفاده npm داشته باشیم که به همراه ریاکت و ریداکس استفاده میشوند.
مبانی ریداکس
ما در این نوشته با یک اپلیکیشن کاملاً ساده ریاکت کار میکنیم تا بتوانیم مبانی ریداکس را نشان داده و توضیح دهیم.
ما قصد داریم یک کامپوننت بسازیم که یک عنصر دکمه بازگشت میدهد و کاربر میتواند روی آن کلیک کند تا مقداری از متن مشخص شود. فهرست فایلهایی که میخواهیم نگاه دقیقی به آنها داشته باشیم، شامل همه فایلهایی میشود که زیر پوشه src قرار دارند. همچنین به بررسی فایلهای rootReducer.js و App.js میپردازیم.
1redux-example
2 node_modules
3 public
4 src
5 actions
6 actions.js
7 types.js
8 components
9 Toggle.js
10 reducers
11 toggle.js
12 App.js
13 index.js
14 rootReducer.js
15 serviceWorker.js
16.gitignore
17package-json.lock
18package.json
19README.md
20yarn.lock
کار خود را با تجزیه کردن اکشنها آغاز میکنیم.
اکشنها
مستندات ریداکس (+) اکشنها را به عنوان «ظرفیت ترابری اطلاعات که دادهها را از اپلیکیشن به store ارسال میکند» تعریف کرده است. فعلاً لازم نیست در مورد واژه store نگران باشید، چون در ادامه همه چیز را توضیح خواهیم داد. مستندات ریداکس اکشنها را به خوبی تعریف کردهاند. اکشنها پیچیده نیستند؛ در واقع اکشن صرفاً یک شیء جاوا اسکریپت ساده است. به مثال زیر توجه کنید:
1// We'll talk about this line of code a little later
2import { TOGGLE_MESSAGE_VISIBILITY } from './types';
3// A basic Redux action (it's just a Javascript object)
4{ type: TOGGLE_MESSAGE_VISIBILITY }
خط 4 کد فوق یک اکشن معتبر ریداکس است. چنان که میبینید این صرفاً یک شیء جاوا اسکریپت است. اکشنها در ریداکس باید یک مشخصه type داشته باشند که نشاندهنده نوع اکشنی است که قرار است اجرا شود. میتوان از اکشنها هر چیزی خواست؛ اما باید کاری که خواسته میشود توضیح داده شود. به جز این میتوان هر مشخصهای که برای یک اپلیکیشن معنیدار است به اکشنها اضافه کرد. در اغلب موارد میبینید که الگوی مهم معرفی شده در خط 1 یعنی گزاره ایمپورت به وفور در کدهای مختلف وجود دارد. توجه داشته باشید که در دایرکتوری اکشنها در پروژه، فایلی به نام type.js داریم که مانند زیر است:
1export const TOGGLE_MESSAGE_VISIBILITY = 'TOGGLE_MESSAGE_VISIBILITY';
ثابتهای نوع اکشن
فایلهایی که در بخش فوق معرفی کردیم، ثابتهای نوع اکشن هستند و دلیل این که برنامهنویسان دوست دارند از آنها استفاده کنند، این است که به سازماندهی کد کمک میکنند. بدین ترتیب همه انواع اکشن در یک فایل نگهداری میشوند و برنامهنویسان میتوانند آنها را در فایلهای دیگر ایمپورت کرده و یا به صورت موردی استفاده کنند. این وضعیت به جلوگیری از بروز باگ در زمان فراخوانی اکشنها از سوی «کاهنده» (Reducer)-ها کمک میکند. این نکته را در بخشی از ذهن خود داشته باشید تا در موقع خود توضیح دهیم که چرا این وضعیت به جلوگیری از بروز باگ در کاهندهها کمک میکند.
بنابراین اکشنهایی داریم که صرفاً اشیای ساده جاوا اسکریپت هستند. اکنون از اکشنها برای ساختن «اکشنساز» (Action Creator) استفاده میکنیم. لطفاً از این که با یک اصطلاح جدید ریداکس مواجه شدهاید، آشفته نشوید. اکشنساز نیز بسیار ساده است. در ادامه نگاهی به یک اکشنساز خواهیم داشت که مربوط به اکشن کدنویسی شده در بخش قبلی است:
1import { TOGGLE_MESSAGE_VISIBILITY } from './types';
2// this is our action creator. it's just a function that returns an object (our action!)
3export function toggleMe() {
4 return {
5 type: TOGGLE_MESSAGE_VISIBILITY,
6 }
7};
همان طور که در کد فوق میبینید، یک اکشنساز صرفاً تابع ساده جاوا اسکریپت است که شیء را بازگشت میدهد. این اکشنسازها توابعی هستند که اکشنها را ایجاد میکنند (بازگشت میدهند). ما باید اکشنساز خود را اکسپورت کنیم، زیرا در نهایت میخواهیم آن را در فایل کامپوننت Toggle.js ایمپورت کنیم تا از آن برای dispatch کردن اکشن TOGGLE_MESSAGE_VISIBILITY استفاده کنیم. اما شاید کمی تند رفتیم، ابتدا باید کاهندهها را بررسی کنیم.
کاهندهها
در اپلیکیشنهای ریداکس، «حالت» (State) اپلیکیشن به صورت یک شیء منفرد ذخیره میشود. هنگامی که یک اکشن به store (این را نیز به زودی تعریف خواهیم کرد) ارسال میشود، کاهندهها به مدیریت شیوه تغییر یافتن حالت در پاسخ به آن اکشن میپردازند. ما تنها رد 1 چیز را در حالت اپلیکیشن خود نگه میداریم و آن این است که آیا پیام ما قابل مشاهده است یا نه. در ادامه نگاهی به فایل toggle.js خواهیم داشت که در دایرکتوری کاهندهها به صورت تو در تو قرار دارد:
1// Now we will get to why this pattern prevents bugs
2import { TOGGLE_MESSAGE_VISIBILITY } from '../actions/types';
3// Here we define the initial state of our application
4// Initially, our message is not visible
5const initialState = {
6 messageVisibility: false,
7}
8// This is our REDUCER
9export default function (state = initialState, action) {
10 // here we pluck off action.type and store it in the const type
11 // using some es6 destructuring
12 const { type } = action;
13switch (type) {
14 case TOGGLE_MESSAGE_VISIBILITY: {
15 return {
16 ...state,
17 messageVisibility: !state.messageVisibility,
18 }
19 }
20 default: {
21 return state
22 }
23 }
24}
تنظیمات پایه
توضیح کد فوق را از خط 1 آغاز میکنیم. به خاطر داشته باشید که گفتیم استفاده از ثابتهای نوع اکشن به جلوگیری از باگ کمک میکنند؟ برای توضیح دلیل این مسئله کد زیر را بدون استفاده از ثابتهای نوع اکشن نوشتهایم. به جای استفاده از ثابتهای نوع اکشن، که به اکشنسازها و کاهندهها ارسال میشوند، از تنظیمات پایه استفاده میکنیم:
1// this is our actions > actions.js file
2export function toggleMe() {
3 return {
4 type: "TOGGLE_MESSAGE_VISIBILITY",
5 }
6};
7// this is our reducers > toggle.js file
8const initialState = {
9 messageVisibility: false,
10}
11export default function (state = initialState, action) {
12 const { type } = action;
13
14 // Notice the TYPO in "TOGLE_MESSAGE_VISIBILITY" instead of "TOGGLE_MESSAGE_VISIBILITY" below
15 switch (type) {
16 case "TOGLE_MESSAGE_VISIBILITY": {
17 return {
18 ...state,
19 messageVisibility: !state.messageVisibility,
20 }
21 }
22 default: {
23 return state
24 }
25 }
26}
در مثال فوق، از ثابتهای نوع اکشن برای ساخت اکشنساز بهره نمیگیریم و به جای آن از تنظیمات پایه استفاده کردهایم. سپس در کاهنده اکشنی را که میخواهیم در ثابت سوئیچ تحریک شود به صورت اشتباه نوشتهایم (ساختار کاهندهها را نیز در ادامه توضیح خواهیم داد). مشکل کد فوق این است که کاری که ما از آن میخواهیم را انجام نمیدهد؛ اما خطایی را نیز تولید نمیکند. کد همچنان اجرا میشود، زیرا این یک رشته معتبر است. با این وجود اگر آن را با استفاده از یک ثابت نوع اکشن بارگذاری میکردیم و سپس آن ثابت را اشتباه مینوشتیم، کامپایلر خطایی صادر میکرد که به ما نشان میداد کجا اشتباه کردهایم.
اینک متوجه شدیم که چرا باید از ثابتهای نوع اکشن استفاده کنیم؛ اما در مورد باقی فایل چه میتوان گفت؟ کار خود را با توصیف آن چه که در مورد حالت ابتدایی میخواهیم، آغاز میکنیم. ما میخواهیم که پیاممان در ابتدا پنهان باشد:
1const initialState = {
2 messageVisibility: false,
3}
این مورد نیز یک مثال کاملاً ساده محسوب میشود. شما احتمالاً در اپلیکیشنهای بزرگتر خود در حالت آغازین، مشخصههای بیشتری خواهید داشت. در نهایت کاهنده واقعی را مورد بررسی قرار میدهیم:
1export default function (state = initialState, action) {
2 const { type } = action;
3switch (type) {
4 case TOGGLE_MESSAGE_VISIBILITY: {
5 return {
6 ...state,
7 messageVisibility: !state.messageVisibility,
8 }
9 }
10 default: {
11 return state
12 }
13 }
14}
تابع کاهنده
توضیح خود را با اکسپورت کردن تابع کاهنده آغاز میکنیم، زیرا در ادامه قصد داریم همه کاهندهها را در فایل rootReducer.js بارگذاری کنیم. سپس پارامتر حالت را به صورت شیء حالت آغازین که در بخش قبلی کدنویسی کردیم تنظیم و سپس به اکشن خود ارسال میکنیم. در خط 2، action.type را استخراج کرده و آن را در ثابت type با استفاده از روش تجزیه ساختار ES6 ذخیره میکنیم.
سپس به بدنه اصلی کاهنده میرسیم که گزاره سوئیچ است. گزاره سوئیچ ما به نوع اکشن نگاه میکند و حالت را بر اساس روشی که میخواهیم اکشن، آن را تغییر دهد تنظیم میکند. در این مثال ساده میخواهیم messageVisibility را از false به true یا برعکس تغییر دهیم و پیام خود را پنهان کنیم. در ادامه نگاهی دقیقتر به کاهنده خودمان خواهیم داشت:
1case TOGGLE_MESSAGE_VISIBILITY: {
2 return {
3 ...state,
4 messageVisibility: !state.messageVisibility,
5 }
6}
state... برای چیست؟
به خاطر داشته باشید که ما هرگز نباید در اپلیکیشنهای ریاکت، state را به صورت مستقیم دستکاری کنیم. ساختار فوق در جاوا اسکریپت ES6 ارائه شده است و دقیقاً همان کار کد زیر را انجام میدهد:
1// This code does the same thing as the code above
2case TOGGLE_MESSAGE_VISIBILITY: {
3 return {
4 Object.assign({}, state, {
5 messageVisibility: !state.messageVisibility,
6 })
7 }
8}
بدین ترتیب یک کپی از حالت ایجاد میشود و مشخصهای که میخواهیم تغییر داده میشود. در ریداکس نمیتوان حالت را تغییر داد؛ بلکه به جای آن باید یک کپی از آن را بازگشت داد.
اکنون که کاهنده تنظیم شده است باید نگاهی به فایل rootReducer.js و فایل App.js داشته باشیم تا همه این موارد را جمعبندی کنیم. به فایل rootReducer.js توجه کنید:
1import { combineReducers } from 'redux';
2import toggle from './reducers/toggle';
3const rootReducer = combineReducers({
4 toggle
5});
6export default rootReducer;
توضیح خود را از ایمپورت کردن متد combineReducers از ریداکس آغاز میکنیم. البته این مثال تا حدودی ساختگی است، زیرا برای متد combineReducers خود، کاهندههای چندگانه نداریم؛ اما فعلاً این موضوع برایمان اهمیتی دارد. سپس کاهنده toggle را ارسال کرده و نتیجه را در یک const به نام rootReducer ذخیره میکنیم که در خط آخر اکسپورت شده است.
اکنون ما همه کاهندههای خود را به صورت ترکیبی در یک const منفرد به نام rootReducer داریم که در اختیار بخشهای دیگر اپلیکیشن قرار دارد. این امر مهمی است، زیرا از این rootReducer برای ایجاد store استفاده خواهیم کرد.
Store ریداکس
هنگامی که از ریداکس استفاده میکنیم، store به نگهداری از حالت اپلیکیشن میپردازد. هر اپلیکیشن ریداکس تنها 1 store منفرد دارد. store در ریداکس موضوع عجیبی است، زیرا امکان دسترسی به حالت اپلیکیشن را از هر کامپوننتی درون اپلیکیشن میدهد. اتصال به store ریداکس کار بسیار آسانی است. به فایل App.js زیر نگاه کنید:
1import React, { Component } from 'react';
2import { Provider } from 'react-redux';
3import { createStore } from 'redux';
4import Toggle from './components/Toggle';
5import rootReducer from './rootReducer';
6const store = createStore(
7 rootReducer
8);
9class App extends Component {
10 render() {
11 return (
12 <Provider store={store}>
13 <Toggle />
14 </Provider>
15 );
16 }
17}
18export default App;
ما کار خود را با ایمپورت کردن ریاکت و کامپوننت از ریاکت آغاز میکنیم که خب چیزی جدیدی در این جا وجود ندارد. سپس Provider را از react-redux ایمپورت میکنیم. ماژول npm به نام react-redux به ما کمک میکند که ریداکس را به آسانی به ریاکت اتصال دهیم. بنابراین تابع createStore را از ریداکس ایمپورت میکنیم و سپس کامپوننت Toggle و rootReducer خود را که در بخش قبلی ایجاد کردیم، ایمپورت میکنیم.
به خاطر داشته باشید، const ما به نام rootReducer همه کاهندهها را در اپلیکیشن در یک کاهنده منفرد نگهداری میکند. بنابراین store را با استفاده از تابع createStore که قبلاً ایمپورت کردهایم میسازیم و آن را به RootReducer ارسال میکنیم. در نهایت در گزاره statement مربوط به تابع render همه اپلیکیشن را درون Provider قرار میدهیم و store خود را به آن ارسال میکنیم.
بدین ترتیب به پایان این مقاله میرسیم. شاید این مقاله یک توصیف طولانی برای یک کاربرد بسیار کوچک محسوب شود؛ اما امیدواریم به تفهیم برخی از مفاهیم پایه ریداکس برای شما کمک کرده باشد. شما میتوانید هرگونه دیدگاه یا پیشنهاد خود را در بخش نظرات با ما در میان بگذارید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- آموزش مقدماتی فریمورک React Native
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- راهنمای مقدماتی ریداکس (Redux) — به زبان ساده
- آموزش ری اکت (React) — مجموعه مقالات مجله فرادرس
==
سلام و وقت بخیر دوست عزیز.
ما هم با شما موافقیم و بر همین مبنا عنوان مطلب را تغییر دادیم.
متشکریم از توجه شما.
ریداکس مناسبتر از ردوکس هست