در قسمت قبل با action ها و subscription ها آشنا شدیم بنابراین دیگر نیازی به فایل redux-basics.js نداریم و آن را می بندیم. ما یک بار redux را در برنامه ساده شمارنده خودمان پیاده سازی می کنیم (سورس کد آن در جلسه اول این فصل در اختیار شما قرار گرفت) و پس از آن به سراغ برنامه همبرگر ساز می رویم تا آن را در همبرگر ساز نیز پیاده کنیم.
برای شروع نیاز به یک store داریم و باید آن را قبل از شروع برنامه مان ایجاد کنیم. به نظر من فایل index.js جای خوبی برای ساخت Store است بنابراین وارد آن شده و redux را وارد آن کنید:
import { createStore } from 'redux';
سپس قبل از mount شدن برنامه این Store را می سازیم:
const store = createStore(); ReactDOM.render(<App />, document.getElementById('root')); registerServiceWorker();
اما از جلسات قبل یاد گرفتیم که برای ایجاد این store به یک reducer نیاز داریم. از آنجا که برنامه های واقعی React دارای reducer های نسبتا شلوغ و بزرگی هستند، ما آن ها را در یک فایل جداگانه تعریف می کنیم. در پوشه src یک پوشه دیگر به نام store ایجاد می کنیم که حاوی فایل reducer.js باشد. درون این فایل یک تابع ساده قرار می گیرد که همان reducer ما است:
const initialState = { counter: 0 } const reducer = (state = initialState, action) => { return state; } export default reducer;
در ابتدا یک state اولیه را در نظر گرفتم که initialState است. سپس یک reducer ساده را نیز تعریف کرده ام که state فعلی را برمی گرداند. این reducer ساده و تکمیل شده ما است.
حالا دوباره وارد فایل index.js شده و این reducer را در آنجا import می کنیم:
import reducer from './store/reducer';
و در نهایت آن را به createStore پاس می دهیم:
import reducer from './store/reducer'; const store = createStore(reducer); ReactDOM.render(<App />, document.getElementById('root')); registerServiceWorker();
در حال حاضر store ما ساخته شده است اما باید کاری کنیم که برنامه react ما به redux متصل شود و کامپوننت های ما بتوانند به این Store دسترسی داشته باشند. برای اتصال store به برنامه react خودمان به یک پکیج دیگر نیاز داریم چرا که redux به تنهایی به react متصل نمی شود. نام این پکیج react-redux است بنابراین آن را از طریق ترمینال نصب می کنیم:
npm install --save react-redux
پس از تکمیل شدن فرآیند نصب می توانیم عنصر مورد نظر خود را از این پکیج وارد فایل index.js کنیم:
import { Provider } from 'react-redux';
در واقع ما برنامه خود را درون این provider قرار می دهیم:
ReactDOM.render(<Provider><App /></Provider>, document.getElementById('root')); registerServiceWorker();
Provider یک کامپوننت کمکی است که به ما اجازه می دهد store خود را وارد کامپوننت ها کنیم. در مرحله بعد باید Store ای را که با createStore ساختیم در قالب یک prop به نام store به آن پاس بدهیم:
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
سوال: چطور داده های store خود (مثلا counter درون initialState) را به کامپوننت های خود مثل Counter.js منتقل کنیم؟
پاسخ: برای انجام این کار باید کامپوننت خود را به store متصل کنیم یا به زبان فنی تر نیاز به یک subscription داریم. البته به جای صدا زدن subscribe از یک روش دیگر استفاده می کنیم که توسط react-redux به ما داده شده است.
وارد فایل Counter.js شده و دستور زیر را در آن تایپ کنید:
import { connect } from 'react-redux';
connect یک تابع است که یک HOC را برمی گرداند و روی دستور export پیاده سازی می شود و مثلا در این فایل علاوه بر Counter دو مقدار دیگر را نیز می گیرد:
بنابراین ابتدا باید مشخص کنیم که چه قسمتی از state برایمان مهم است. ما این کار را در آخر فایل Counter.js و قبل از دستور export انجام می دهیم:
const mapStateToProps = state => { return { } } export default Counter;
برای مشخص کردن state مورد نظر باید مانند بالا یک متغیر تعریف کنیم که در واقع یک تابع است و state را به عنوان پارامتر خود می گیرد. این تابع مشخص می کند که state موجود در redux چطور باید به شکل props در اختیار کامپوننت فعلی (Counter.js) قرار بگیرد. باید توجه داشته باشید که redux یک پکیج مستقل از react است بنابراین state داخل آن به صورت state معمولی در دسترس کامپوننت ما نیست بلکه به صورت prop به کامپوننت ها پاس داده می شود. state همیشه چیزی است که از درون خود کامپوننت قابل تغییر است اما redux درون کامپوننت ما نیست.
درون این تابع و در قسمت return باید نام prop های مورد نظر خود مان را تعریف کنیم:
const mapStateToProps = state => { return { ctr: state.counter } }
من در اینجا یک prop به نام ctr تعریف کرده ام که مخفف counter یا شمارنده است. سپس با همان state ای که به عنوان آرگومان دریافت کرده ایم به state سراسری خودمان دسترسی پیدا کرده (همان initialState در فایل reducer.js) و از آنجا مقدار counter را دریافت کرده ایم. حالا می توانیم از ctr به عنوان یک prop در کامپوننت Counter.js استفاده کنیم! البته قبل از آن باید از connect روی دستور export به شکل زیر استفاده کنیم:
const mapStateToProps = state => { return { ctr: state.counter } } export default connect(mapStateToProps)(Counter);
دستور connect متغیر mapStateToProps را به کامپوننت counter متصل می کند تا به prop های آن دسترسی داشته باشیم. توجه کنید که باید از ساختار بالا و به همین شکل استفاده کنید. برای تست این موضوع وارد قسمت JSX (تابع render) می شوم و به جای state.counter مقدار ctr را به <CounterOutput> پاس می دهم:
<CounterOutput value={this.props.ctr} />
حالا اگر دستور npm start را اجرا کنیم و برنامه را در مرورگر باز کنیم، می بینیم که شمارنده ما روی عدد صفر قرار گرفته است (چرا که counter در initialState را برابر صفر گذاشته ایم). همچنین اگر روی دکمه های برنامه مثل Add 5 یا Increment کلیک کنیم هیچ اتفاقی نمی افتد. چرا؟ به دلیل اینکه این دکمه ها state درون کامپوننت Counter.js را تغییر می دهند اما برای نمایش شمارنده از state داخل redux استفاده کرده ایم! در قسمت بعد کاری می کنیم که این دکمه ها با استفاده از action های خود State داخل redux را تغییر دهند.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.