پس از بررسی ساختار کتابخانهی Redux به صورت مستقل و متکی به خود، اکنون در این قسمت، نحوهی اتصال آنرا به برنامههای React بررسی میکنیم.
نصب پیشنیازها
میتوان همانند
قسمت قبل، تمام کارها را با کتابخانهی redux انجام داد و یا میتوان قسمت به روز رسانی UI آنرا و همچنین مدیریت state را به کتابخانهی ساده کنندهی دیگری به نام react-redux واگذار کرد. به همین جهت در ادامهی همان برنامهی
قسمت قبل، دو کتابخانهی redux و همچنین react-redux را به همراه types آن نصب میکنیم (نصب types، سبب ارائهی intellisense بهتری در VSCode میشود؛ حتی اگر نخواهیم با TypeScript کار کنیم).
برای این منظور پس از باز کردن پوشهی اصلی برنامه توسط VSCode، دکمههای ctrl+` را فشرده (ctrl+back-tick) و دستورات زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save redux react-redux
> npm install --save-dev @types/react-redux
به علاوه در ادامه توئیتر بوت استرپ 4 را نیز نصب میکنیم:
> npm install --save bootstrap
سپس برای افزودن فایل bootstrap.css به پروژهی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
این import به صورت خودکار توسط webpack ای که در پشت صحنه کار bundling & minification برنامه را انجام میدهد، مورد استفاده قرار میگیرد.
معرفی ساختار ابتدایی برنامه
برنامهای را که در این قسمت بررسی میکنیم، ساختار بسیار سادهای را داشته و به همراه دو دکمهی افزایش و کاهش مقدار یک شمارشگر است؛ به همراه دکمهی برای به حالت اول در آوردن آن. هدف اصلی دنبال شدهی در اینجا نیز نحوهی برپایی redux و همچنین react-redux و اتصال آنها به برنامهی React جاری است:
به همین جهت ابتدا کامپوننت جدید src\components\counter.jsx را به نحو زیر تشکیل میدهیم تا markup ابتدایی فوق را به همراه سه دکمه و یک span، برای نمایش مقدار شمارشگر، رندر کند:
import React, { Component } from "react";
class Counter extends Component {
render() {
return (
<section className="card mt-5">
<div className="card-body text-center">
<span className="badge m-2 badge-primary">0</span>
</div>
<div className="card-footer">
<div className="d-flex justify-content-center align-items-center">
<button className="btn btn-secondary btn-sm">+</button>
<button className="btn btn-secondary btn-sm m-2">-</button>
<button className="btn btn-danger btn-sm">Reset</button>
</div>
</div>
</section>
);
}
}
export default Counter;
سپس المان آنرا جهت نمایش در برنامه، به فایل src\App.js اضافه میکنیم:
import "./App.css";
import React from "react";
import Counter from "./components/counter";
function App() {
return (
<main className="container">
<Counter />
</main>
);
}
export default App;
پوشه بندی مخصوص برنامههای مبتنی بر Redux
هدف ما در ادامه ایجاد یک store مخصوص redux است و سپس اتصال آن به کامپوننت شمارشگر برنامه. به همین جهت نیاز به 4 پوشهی جدید، برای مدیریت بهتر برنامه خواهیم داشت:
- پوشه constants: برای اینکه نام رشتهای نوع اکشنهای مختلف را بتوانیم در قسمتهای مختلف برنامه استفاده کنیم، بهتر است فایل جدید src\actions\index.js را ایجاد کرده و این ثوابت را داخل آن export کنیم.
- پوشهی actions: در فایل جدید src\actions\index.js، تمام متدهای ایجاد کنندهی شیء خاص action، که در
قسمت قبل در مورد آن بحث شد، قرار میگیرند. نمونهی آن، متد createAddAction
قسمت قبل است.
- پوشهی reducers: تمام توابع reducer برنامه را در فایلهای مجزایی در پوشهی reducers قرار میدهیم. سپس در فایل src\reducers\index.js با استفاده از متد combineReducer آنها را یکی کرده و به متد createStore ارسال میکنیم.
- پوشهی containers: این پوشه جائی است که کار فراخوانی متد connect کتابخانهی react-redux به ازای هر کامپوننت استفاده کنندهی از redux store، صورت میگیرد.
این موارد را با جزئیات بیشتری در ادامه بررسی میکنیم.
ایجاد نام نوع اکشن متناظر با دکمهی افزودن مقدار
میخواهیم با کلیک بر روی دکمهی +، مقدار شمارشگر افزایش یابد. به همین جهت نیاز به یک نام وجود دارد تا در تابع Reducer متناظر و قسمتهای دیگر برنامه، بتوان بر اساس آن، این اکشن خاص را شناسایی کرد و سپس عکس العمل نشان داد. به همین جهت فایل جدید src\constants\ActionTypes.js را ایجاد کرده و به صورت زیر تکمیل میکنیم:
export const Increment = "Increment";
البته هرچند مرسوم است نام و مقدار این نوع ثوابت را تشکیل شدهی از حروف بزرگ، معرفی کنند ولی این موضوع اختیاری است.
ایجاد متد Action Creator
در
قسمت قبل مشاهده کردیم که شیء ارسالی به یک reducer از طریق dispatch یک action خاص، دارای فرمت ویژهی زیر است:
{
type: "ADD",
payload: {
amount // = amount: amount
},
meta: {}
}
به همین جهت برای نظم بخشیدن به تعریف این نوع اشیاء و یکدست سازی آنها، فایل جدید src\actions\index.js را ایجاد کرده و آنرا به صورت زیر تکمیل میکنیم:
import * as types from "../constants/ActionTypes";
export const incrementValue = () => ({ type: types.Increment });
همانطور که ملاحظه میکنید در این متد، فعلا فقط نام رشتهای نوع این اکشن، بیشتر مدنظر است تا بر اساس action.type رسیده در reducer متناظر با آن، عملی رخ دهد. بنابراین فقط قسمت type آنرا مقدار دهی کردهایم. مقدار ثابت رشتهای types.Increment نیز از فایل مجزای src\constants\ActionTypes.js که پیشتر تعریف کردیم، تامین شدهاست.
ایجاد تابع reducer مخصوص افزودن مقدار
ابتدا فایل جدید src\reducers\counter.js را با محتوای زیر ایجاد میکنیم:
import * as types from "../constants/ActionTypes";
const initialState = {
count: 0
};
export default function counterReducer(state = initialState, action) {
if (action.type === types.Increment) {
return {
count: state.count + 1
};
}
return state;
}
- اگر دقت کرده باشید، کامپوننت شمارشگر این قسمت، دارای state نیست و همچنین نمیخواهیم هم که دارای state باشد؛ چون قرار است توسط redux مدیریت شود. به همین جهت state اولیه را به صورت initialState که محتوای یک شیء با خاصیت count با مقدار اولیهی صفر است، خارج از کلاس کامپوننت، ایجاد کردهایم.
- سپس میخواهیم رویداد کلیک بر روی دکمه + را مدیریت کنیم. به همین جهت نیاز به یک اکشن جدید به نام Increment داریم که توسط مقدار ثابت رشتهای types.Increment، از فایل مجزای src\constants\ActionTypes.js، تامین میشود.
- پس از مشخص کردن نوع action ای که قرار است مدیریت شود و همچنین ایجاد متدی برای تولید شیء حاوی اطلاعات آن که در فایل src\actions\index.js قرار دارد، اکنون میتوان متد reducer را که state و action را دریافت میکند و سپس state جدیدی را بر اساس action.type دریافتی و در صورت نیاز بازگشت میدهد، ایجاد کرد. این متد بررسی میکند که آیا action.type رسیده همان ثابت Increment است؟ اگر بله، بجای تغییر مستقیم state.count، یک شیء جدید را بازگشت میدهد. البته روش صحیحتر اینکار را در
قسمت اول این سری با معرفی روشهایی برای کپی اشیاء و آرایهها، بررسی کردیم. در اینجا جهت سادگی بیشتر، یک شیء کاملا جدید را دستی ایجاد میکنیم. در آخر اگر action.type رسیده قابل پردازش نبود، همان state ابتدایی دریافتی را بازگشت میدهیم تا در صورت وجود چندین reducer تعریف شدهی در سیستم، زنجیرهی آنها قابل پردازش باشد. این مورد را در
قسمت قبل، ذیل عنوان «بررسی تابع combineReducers با یک مثال» بیشتر بررسی کردیم.
پس از ایجاد reducer اختصاصی عمل افزودن مقدار شمارشگر، فایل جدید src\reducers\index.js را نیز با محتوای زیر ایجاد میکنیم:
import { combineReducers } from "redux";
import counterReducer from "./counter";
const rootReducer = combineReducers({
counterReducer
});
export default rootReducer;
کار این فایل، مدیریت مرکزی تمام reducerهای سفارشی تعریف شدهی در برنامهاست. لیست آنها را به متد combineReducers ارسال کرده و در نهایت یک rootReducer ترکیب شدهی از تمام آنها را دریافت میکنیم.
ایجاد store مخصوص Redux
تا اینجا رسیدیم به یک rootReducer متشکل از تمام reducerهای سفارشی برنامه. اکنون بر اساس آن در فایل src\index.js، یک store جدید را ایجاد میکنیم:
import { createStore } from "redux";
import reducer from "./reducers";
//...
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
//...
نکته 1: چون شیء rootReducer در فایل src\reducers\index.js واقع شدهاست، دیگر در حین import، نیازی به ذکر نام فایل index آن نیست.
نکته 2: در اینجا روش فعالسازی افزونهی
redux-devtools را نیز ملاحظه میکنید. ابتدا بررسی میشود که آیا متد ویژهی فراخوانی این افزونه وجود دارد یا خیر؟ اگر بله، فراخوانی میشود. بدون این پارامتر دوم، افزونهی redex dev tools، هیچ خروجی را نمایش نخواهد داد.
اتصال React به Redux
کتابخانهی react-redux تنها به همراه دو شیء مهم connect و Provider است. شیء Provider آن شبیه به Context API خود React است و هدف آن، ارسال ارجاعی از store ایجاد شده، به برنامهی React است. پس از ایجاد store در فایل src\index.js، اکنون نوبت به اتصال آن به برنامهی React ای جاری است. به همین جهت در بالاترین سطح برنامه، ابتدا شیء کامپوننت App را با شیء Provider محصور میکنیم:
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./reducers";
// ...
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
کامپوننت Provider، از طریق props خود نیاز به دریافت store تعریف شده را دارد. به این ترتیب هر کامپوننتی که در درخت کامپوننتهای App قرار میگیرد، میتواند با redux store کار کند.
تامین state کامپوننت شمارشگر از طریق props
همانطور که عنوان شد، کامپوننت Counter به همراه state نیست و ما قصد نداریم در آن از state خود React استفاده کنیم؛ البته فلسفهی آنرا در
قسمت اول این سری بررسی کردیم و همچنین اگر کامپوننتی نیاز به اشتراک گذاری اطلاعات خودش را با لایههای زیرین یا بالاتر از خود ندارد، شاید اصلا نیازی به Redux نداشته باشد و همان state استاندارد React برای آن کافی است. بنابراین میتوان برنامهای را داشت که ترکیبی از state استاندارد React، در کامپوننتهای متکی به خود و Redux، در کامپوننتهایی که باید اطلاعاتی را با هم به اشتراک بگذارند، باشد. برای مثال، کامپوننت مثال جاری، واقعا نیازی را به Redux، برای مدیریت حالت خود، ندارد؛ هدف ما در اینجا بررسی نحوهی برقراری ارتباطات یک سیستم مبتنی بر Redux، در برنامههای React است.
بنابراین در اینجا و کامپوننتی که قرار است از Redux برای مدیریت حالت خود استفاده کند، هر اطلاعاتی که به آن از طریق react-redux store وارد میشود، از طریق props به آن ارسال خواهد شد. برای مثال در اینجا مقدار count، از طریق props خوانده میشود و همچنین امکان ارسال action ای خاص به متد reducer تعریف شده نیز باید تعریف شود. بنابراین در ادامه نیاز داریم تا یک کامپوننت React را به redux store متصل کنیم. برای این منظور فایل جدید src\containers\Counter.js را با محتوای زیر ایجاد میکنیم:
import { connect } from "react-redux";
import { incrementValue } from "../actions";
import Counter from "../components/counter";
const mapStateToProps = state => {
return state;
};
const mapDispatchToProps = dispatch => {
return {
increment() {
dispatch(incrementValue());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
ابتدا متد connect را از react-redux دریافت میکنیم. connect خود متدی است که منتظر یک کامپوننت React است؛ مانند Counter. همچنین به عنوان پارامتر، توابعی را دریافت میکند که اطلاعات redux store را به کامپوننت، نگاشت میکنند؛ مانند props و actions. در اینجا دو تابع نگاشت state به props و همچنین dispatch به props را ملاحظه میکنید (توابع mapStateToProps و mapDispatchToProps)؛ هرچند الزامی نیست، ولی بهتر است از همین روش نامگذاری استفاده شود.
زمانیکه در مورد store در redux صحبت میشود، داخل آن یک شیء بزرگ state قرار گرفتهاست که حاوی کل state برنامهاست. اما شاید هر کامپوننت به تمام آن نیازی نداشته باشد. برای مثال شاید کامپوننت شمارشگر، اهمیتی به اطلاعات خطاهای سیستم و یا کاربر وارد شدهی به سیستم که در شیء کلی state موجود در store وجود دارند، ندهد. به همین جهت متد mapStateToProps، کل state برنامه را دریافت کرده و به ما اجازه میدهد تا تنها اطلاعاتی را که از آن نیاز داریم، به صورت props دریافت کنیم. به این ترتیب از رندر مجدد این کامپوننت نیز جلوگیری خواهد شد؛ چون این کامپوننت دیگر وابستهی به تغییرات سایر اجزای کل state برنامه، نخواهد بود و اگر آنها تغییر کردند، این کامپوننت رندر مجدد نخواهد شد.
بنابراین میتوان متد mapStateToProps را به صورت کلی زیر نیز تعریف کرد:
const mapStateToProps = (state) => { return state };
هرچند این روش در مثال ما بدون مشکل کار میکند، اما چون کل state را دریافت میکند، مشکل رندر مجدد کامپوننت را به ازای هر تغییری در state کلی برنامه به همراه خواهد داشت.
یک نکته: اگر کامپوننتی نیاز به تامین state خود را از طریق props نداشت و فقط کارش صدور رخدادها است، میتوان پارامتر اول متد connect را نال وارد کرد.
پارامتر dispatch متد mapDispatchToProps، به متد store.dispatch اشاره میکند. بنابراین توسط آن امکان ارسال actions را میسر کرده و میتوان state را توسط reducerهای تعریف شده، تغییر داد که در نتیجهی آن props جدیدی به کامپوننت منتقل میشوند. این تابع نیز یک شیء را باز میگرداند. این شیء را فعلا با یک متد دلخواه مقدار دهی میکنیم که توسط پارامتر dispatch رسیدهی به آن، متد action creator تعریف شدهی در فایل src\actions\index.js را به نام incrementValue، فراخوانی میکند؛ دقیقا عملی شبیه به فراخوانی store.dispatch(createAddAction(2)) در
قسمت قبل که از آن برای ارسال یک اکشن، به reducer متناظری استفاده شد.
یک نکته: اگر کامپوننتی کار صدور رخدادها را انجام نمیدهد، میتوان پارامتر دوم متد connect را بطور کامل حذف کرد و قید نکرد.
استفاده از کامپوننت جدید خروجی متد connect، جهت تامین props کامپوننت شمارشگر
در انتهای فایل src\components\counter.jsx، چنین سطری درج شدهاست:
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
این شیء حاصل، به خودی خود، سبب بروز تغییری در کامپوننت شمارشگر نمیشود. بلکه یک کامپوننت دربرگیرندهی کامپوننت Counter را ایجاد میکند (به همین جهت آنرا در پوشهی containers یا دربرگیرندهها قرار دادیم). بنابراین برای استفادهی از آن، به کامپوننت src\App.js مراجعه کرده و جائیکه المان کامپوننت Counter قبلی درج شده، آنرا به صورت زیر تغییر میدهیم:
import "./App.css";
import React from "react";
import CounterContainer from "./containers/Counter";
function App() {
return (
<main className="container">
<CounterContainer />
</main>
);
}
export default App;
ابتدا کامپوننت جدید CounterContainer را که تبادل اطلاعات بین کامپوننت Counter اصلی و state و action مخزن redux را برقرار میکند، import کرده و سپس المان جدید آنرا جایگزین المان کامپوننت شمارشگر اصلی میکنیم.
اکنون کامپوننت شمارشگر src\components\counter.jsx، دو شیء را از طریق props دریافت میکند؛ یکی کل state است که خاصیت count داخل آن قرار دارد و از طریق mapStateToProps تامین میشود. دیگری متد increment ای است که در متد mapDispatchToProps تعریف کردیم و کار صدور رخدادی را به reducer متناظر، انجام میدهد. به همین جهت تغییرات ذیل را در کامپوننت Counter اعمال میکنیم:
import React, { Component } from "react";
class Counter extends Component {
render() {
console.log("props", this.props);
const {
counterReducer: { count },
increment
} = this.props;
return (
<section className="card mt-5">
<div className="card-body text-center">
<span className="badge m-2 badge-primary">{count}</span>
</div>
<div className="card-footer">
<div className="d-flex justify-content-center align-items-center">
<button className="btn btn-secondary btn-sm" onClick={increment}>
+
</button>
<button className="btn btn-secondary btn-sm m-2">-</button>
<button className="btn btn-danger btn-sm">Reset</button>
</div>
</div>
</section>
);
}
}
export default Counter;
لاگ اولین بار دریافت this.pros این کامپوننت که اکنون توسط دربرگیرندهی آن ارائه میشود، به صورت زیر است:
به همین جهت، خاصیت تو در توی this.props.counterReducer.count و همچنین اشارهگر به متد increment، توسط Object Destructuring به صورت زیر از this.props دریافتی، تجزیه شدهاند:
const {
counterReducer: { count },
increment
} = this.props;
سپس مقدار count، توسط span نمایش داده و همچنین دکمه + را به صورت onClick={increment} تکمیل کردهایم تا با کلیک بر روی آن، متد increment که در حقیقت معادل فراخوانی store.dispatch(incrementValue()) است، اجرا شود. حاصل آن، افزایش مقدار شمارشگر است:
جزئیات کار با Redux store را نیز میتوان در افزونهی redux dev tools مشاهده کرد:
این افزونه در نوار ابزار پایین آن، امکان export کل state و سپس import و بازیابی آنرا نیز به همراه دارد.
دریافت props از طریق کامپوننت دربرگیرنده و ارسال آن به کامپوننت اصلی
فرض کنید نیاز باشد تا اطلاعاتی را به صورت متداول React از طریق props، به کامپوننت دربرگیرندهی کامپوننت شمارشگر ارسال کرد:
function App() {
const prop1 = 123
return (
<main className="container">
<CounterContainer prop1={prop1} />
</main>
);
}
برای دسترسی به آن، پارامتر دومی به متد mapStateToProps به نام ownProps اضافه میشود که حاوی props ارسالی به کامپوننت container است:
const mapStateToProps = (state, ownProps) => {
console.log("mapStateToProps", { state, ownProps });
return state;
};
در این حالت اگر نیاز به انتقال آن به کامپوننت اصلی بود، میتوان شیء بازگشت داده شدهی از mapStateToProps را به همراه یک سری خواص سفارشی دریافتی از ownProps، تعریف کرد.
پیاده سازی دکمهی کاهش مقدار شمارشگر
پس از آشنایی با روش کلی برقراری اتصالات سیستم react-redux، پیاده سازی دکمهی کاهش مقدار شمارشگر بسیار سادهاست و شامل مراحل زیر است:
1) ایجاد نام نوع اکشن متناظر با دکمهی کاهش مقدار
به فایل src\constants\ActionTypes.js، نوع جدید کاهشی را اضافه میکنیم:
export const Decrement = "Decrement";
2) ایجاد متد Action Creator
در فایل src\actions\index.js، متد ایجاد کنندهی شیء اکشن ارسالی به reducer متناظری را تعریف میکنیم تا بتوان بر اساس نوع آن در reducer کاهشی، منطق کاهش را پیاده سازی کرد:
export const decrementValue = () => ({ type: types.Decrement });
3) ایجاد تابع reducer مخصوص کاهش مقدار
اکنون در فایل src\reducers\counter.js، بر اساس نوع شیء رسیده، تصمیم به کاهش یا افزایش مقدار موجود در state گرفته میشود:
export default function counterReducer(state = initialState, action) {
// ...
if (action.type === types.Decrement) {
return {
count: state.count - 1
};
}
return state;
}
4) تامین state کامپوننت شمارشگر از طریق props
در ادامه نیاز است بتوان اکشن کاهش را به این reducer ارسال کرد. به همین جهت به کامپوننت دربرگیرندهی کامپوننت شمارشگر در فایل src\containers\Counter.js مراجعه کرده و به شیء خروجی متد mapDispatchToProps، متد کاهش را اضافه میکنیم:
import { decrementValue, incrementValue } from "../actions";
// ...
const mapDispatchToProps = dispatch => {
return {
// ...
decrement() {
dispatch(decrementValue());
}
};
};
5) استفاده از نتایج دریافتی از props
در آخر به فایل src\components\counter.jsx مراجعه کرده و اشارهگر به متد decrement را از طریق this.props دریافت میکنیم:
const {
// ...
decrement
} = this.props;
سپس آنرا به onClick دکمهی کاهش، انتساب خواهیم داد:
<button
className="btn btn-secondary btn-sm m-2"
onClick={decrement}
>
-
</button>
به عنوان تمرین، پیاده سازی دکمهی Reset را نیز انجام دهید که جزئیات آن بسیار شبیه به دو مثال قبلی افزودن و کاهش مقدار شمارشگر است.
بهبود کیفیت کدهای کامپوننت دربرگیرندهی کامپوننت Counter
متد mapDispatchToProps فایل src\containers\Counter.js اکنون چنین شکلی را پیدا کردهاست:
const mapDispatchToProps = dispatch => {
return {
increment() {
dispatch(incrementValue());
},
decrement() {
dispatch(decrementValue());
}
};
};
میتوان با استفاده از تابع bindActionCreators که در
قسمت قبل در مورد آن بحث شد، تعریف آنرا به صورت زیر خلاصه کرد:
import { bindActionCreators } from "redux";
// ...
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
incrementValue,
decrementValue
},
dispatch
);
};
با استفاده از تابع bindActionCreators کتابخانهی redux، میتوان تمام action creators واقع در فایل src\actions\index.js را به صورت یک شیء به آن ارسال کرد و پارامتر دوم آنرا نیز به store.dispatch یا در اینجا به همان dispatch دریافتی توسط پارامتر dispatch متد mapDispatchToProps، تنظیم کرد. البته در این حالت props دریافتی در کامپوننت شمارشگر به صورت زیر تغییر میکنند:
به همین جهت نیاز است در متد رندر کامپوننت src\components\counter.jsx، نامهایی را که به متدهای action creator اشاره میکنند، به صورت زیر تغییر داد:
const {
counterReducer: { count },
incrementValue,
decrementValue
} = this.props;
و همچنین نامهای منتسب به onClickها را نیز بر این اساس، اصلاح کرد.
روش دوم: در نگارشهای اخیر react-redux میتوان متد mapDispatchToProps را به صورت زیر نیز خلاصه و تعریف کرد که بسیار سادهتر است:
const mapDispatchToProps = {
incrementValue,
decrementValue
};
البته در این حالت نیز مابقی آن که شامل تغییر نامها میشود، یکسان است.
همچنین بجای بازگشت کل state در متد mapStateToProps، میتوان تنها خواص مدنظر را بازگشت داد:
const mapStateToProps = state => {
//return state;
return {
count: state.counterReducer.count
};
};
در این حالت props ارسالی به کامپوننت یک چنین شکلی را پیدا میکنند:
بنابراین باید در متد رندر کامپوننت شمارشگر، خاصیت count را به صورت معمولی دریافت کرد:
const {
//counterReducer: { count },
count,
incrementValue,
decrementValue
} = this.props;
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
state-management-redux-mobx-part03.zip