آموزش ریداکس در ریاکت با مثال عملی [React-Redux]

سلام دوستان، با آموزش ریداکس در ریکت با یک مثال عملی، در خدمتتان هستیم.
سورس کد پروژه این مقاله را می توانید با مراجعه به گیتهاب راهکارینو مشاهده و دانلود نمایید.
در این مقاله می خواهیم ریداکس را در دو حالت کلاس کامپوننت و فانکشن کامپوننت در قالب یک مثال عملی آموزش دهیم. خروجی کار به شکل زیر خواهد بود:
در این مثال، یک شمارنده یا Counter ساده نوشتیم که در دو حالت کلاس کامپوننت و فانکشنال کامپوننت با ریداکس کار می کند. لطفا قدم های زیر را همراه با مقاله انجام دهید:
نصب و راه اندازی پروژه react redux:
ابتدا با دستور npx create-react-app redux-counter یک پروژه جدید بنام redux-counter ایجاد می کنیم.
npx create-react-app redux-counter
سپس با دستور cd وارد فولدر پروژه می شویم:
cd redux-counter
در مرحله بعد، به منظور استفاده از redux باید دو لایبرری redux و react-redux را نصب کنیم.
npm install redux react-redux # or yarn add redux react-redux
نکته: در تصویر زیر، جریان داده یا Data Flow ریداکس را مشاهده می کنید:
تعریف store در ریداکس:
برای شروع کار با ریداکس نیاز به تعریف store داریم. دقت کنید که store را می توان در فایل index تعریف کرد و همچنین می توان در یک فولدر بنام store تعریف کرد. با توجه به اینکه پروژه این مقاله مقیاس کوچکی دارد، store را در خود فایل index تعریف می کنیم. به این منظور createStore را از کتابخانه redux ایمپورت کنید:
import {createStore} from 'redux'
سپس در بالای دستور ReactDOM.render دستور زیر را درج کنید:
const store = createStore();
در مرحله بعد برای اینکه کل اپلیکیشن ما از ریداکس بهره مند شود، باید Provider را از کتابخانه react-redux ایمپورت کنیم و کامپوننت App را به شکل زیر درون کامپوننت Provider قرار دهیم. علاوه بر این باید برای Provider یک props بنام store تعریف شود.
import {Provider} from 'react-redux'
<Provider store={store}> <App /> </Provider>
دقت کنید که تمام کدهای فوق باید در فایل index.js تعریف شود.
تعریف action در ریداکس:
همانطور که در اولین تصویر مقاله مشاهده کردید، دکمه های افزایش و کاهش شمارنده شامل چهار اکشن 1- و 1+ و 5- و 5+ می باشد. پس در کل 4 تا اکشن داریم.
یک فولدر بنام actions در فولدر src بسازید و درون آن یک فایل بنام index.js تعریف کنید. کدهای زیر را در فایل ایندکس درج کنید:
export const INCREMENT = 'INCREMENT' export const DECREMENT = 'DECREMENT' export const ADD5 = 'ADD5' export const SUB5 = 'SUB5'
تعریف اکشن در یک فایل مجزا، این مزیت را دارد که همه اکشن های ما در یکجا تعریف می شوند و اگر در آینده بخواهیم نام یکی از آنها را تغییر دهیم، براحتی کافیست به این فایل مراجعه کرد و تغییرات لازم را انجام داد.
تعریف کامپوننت های Counter:
کامپوننت CounterClass شامل یک متن و دو دکمه است. برای نمایش مقدار counter و افزایش/کاهش یک واحدی مقدار counter.
یک فایل بنام CounterClass در فولدر src تعریف کنید و کدهای زیر را برای آن در نظر بگیرید:
import React from 'react' import * as actionTypes from './actions' class CounterClass extends React.Component { render() { return ( <> <h3>Counter in Class Component:</h3> <p>Counter: </p> <button>+</button> <button>-</button> </>) } } export default CounterClass
کامپوننت CounterFunction شامل یک متن و 4 دکمه است. برای نمایش مقدار counter و افزایش/کاهش یک واحدی و 5 واحدی مقدار counter.
یک فایل بنام CounterFunction در فولدر src تعریف کنید و کدهای زیر را برای آن در نظر بگیرید:
import { useSelector, useDispatch } from "react-redux"; import * as actionTypes from "./actions"; const CounterFunction = () => { return ( <> <h3>Counter in Functional Component</h3> <p> Counter:</b> </p> <button>+1</button> <button>-1</button> <button>+5</button> <button>-5</button> </> ); }; export default CounterFunction;
تعریف reducer در ریداکس:
قدم بعدی تعریف reducer است. reducer یک تابع است که دو آرگومان state و action را می گیرد و بر اساس مقدار action.type عملیاتی بر روی state انجام می شود و مقدار نهایی return می شود. ساختار کلی reducer در ریداکس به شکل زیر است:
// Use the initialState as a default value export default function appReducer(state = initialState, action) { // The reducer normally looks at the action type field to decide what happens switch (action.type) { // Do something here based on the different types of actions default: // If this reducer doesn't recognize the action type, or doesn't // care about this specific action, return the existing state unchanged return state } }
و اما در این مثال، به منظور تعریف reducer ابتدا یک پوشه بنام reducers در کنار actions بسازید و در یک فایل بنام counter.js کدهای زیر را تعریف کنید:
import * as actionTypes from "../actions"; const counter = (state = 0, action) => { switch (action.type) { case actionTypes.INCREMENT: return state + 1; case actionTypes.DECREMENT: return state - 1; case actionTypes.ADD5: return state + action.payload; case actionTypes.SUB5: return state - action.payload; default: return state; } }; export default counter;
توضیح کدهای فوق:
ابتدا action type ها را import کرده ایم و سپس در یک ساختار switch-case ، بر اساس مقدار action.type ، مقدار state بازگشتی را مشخص کرده ایم. برای دو نوع اکشن INCREMENT و DECREMENT فقط لازم است state جاری را یکواحد کم یا زیاد کنیم. اما برای دو نوع اکشن ADD5 و SUB5 به payload هم نیاز است. payload در این مثال عدد 5 است. یعنی state جاری را 5 واحد کم یا زیاد می کنیم. در نهایت هم در حالت default مقدار state جاری را return می کنیم.
در ادامه، باید در فولدر reducers یک فایل بنام index.js بسازید و محتوای زیر را برای آن تعریف کنید:
import {combineReducers} from 'redux' import counter from './counter' const rootReducer = combineReducers({ counter }) export default rootReducer
توضیح کدهای فوق:
در پروژه های واقعی، معمولا ما بیش از یک reducer خواهیم داشت. در فایل index باید تمام این reducer ها را با یکدیگر ترکیب یا combine کنیم و بعنوان خروجی فایل index در نظر بگیریم. در این مثال ما فقط یک reducer داریم بنام counter. در خط اول کدهای فوق، combineReducers را از کتابخانه redux ایمپورت کرده ایم و سپس آن را با آرگومان ورودی counter فراخوانی کرده ایم و در متغیر rootReducer قرار داده ایم. در نهایت هم rootReducer را return کرده ایم.
درج reducer در store:
قدم بعدی اینست که این rootReducer ایجاد شده را به store پاس دهیم. پس در فولدر src فایل index.js را باز کرده و سپس rootReducer را import کنید:
import rootReducer from './reducers'
حالا باید آن را بعنوان آرگومان ورودی createStore در نظر بگیرید:
const store = createStore(rootReducer)
سورس کد نهایی فایل index.js بصورت زیر است:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import {createStore} from 'redux' import {Provider} from 'react-redux' import rootReducer from './reducers' const store = createStore(rootReducer) ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
استفاده از ریداکس در کلاس کامپوننت CounterClass:
ابتدا باید action type ها را از فایل مربوطه import کنیم:
import * as actionTypes from './actions'
سپس توسط دو متد mapStateToProps و mapDispatchToProps می خواهیم state و dispatch واقع در store ریداکس را به props کامپوننت تبدیل کنیم. به این معنی که بوسیله props بتوانیم به state ها و dispatch های ریداکس دسترسی داشته باشیم. به این منظور در انتهای فایل CounterClass و قبل از دستور export، قطعه کد زیر را تعریف کنید:
const mapStateToProps = (state) => { return { counter: state.counter } }
در کد بالا، state.counter را به پراپس counter تبدیل کرده ایم و در واقع counter state معادل counter props شده است.
حالا کد زیر را برای dispatch تعریف کنید:
const mapDispatchToProps = (dispatch) => { return { onIncrement: () => dispatch({type: actionTypes.INCREMENT}), onDecrement: () => dispatch({type: actionTypes.DECREMENT}) } }
در کد بالا، توسط متد mapDispatchToProps اکشن تایپ های INCREMENT و DECREMENT را به ترتیب به متدهای onIncrement و onDecrement تبدیل کرده ایم که از طریق props قابل دسترسی هستند.
سپس باید توسط متد connect کامپوننت CounterClass را به redux store متصل کنیم. برای اینکار ابتدا connect را از کتابخانه react-redux ایمپورت کنید:
import {connect} from 'react-redux'
سپس دستور آخر یعنی export را به شکل زیر تعریف کنید:
export default connect(mapStateToProps, mapDispatchToProps)(CounterClass)
اکنون در مرحله آخر باید تابع render در کامپوننت CounterClass را به شکل زیر تغییر دهیم:
render() { return ( <> <h3>Counter in Class Component:</h3> <p>Counter: <b>{this.props.counter}</b></p> <button onClick={() => this.props.onIncrement()}>+</button> <button onClick={() => this.props.onDecrement()}>-</button> </>) }
مشاهده می کنید که مقدار counter برابر است با this.props.counter و برای رویداد onClick دو دکمه کاهش/افزایش شمارنده، متدهای this.props.onIncrement و this.props.onDecrement در نظر گرفتیم.
سورس کد کلی کامپوننت CounterClass بصورت زیر است:
import React from 'react' import {connect} from 'react-redux' import * as actionTypes from './actions' class CounterClass extends React.Component { render() { return ( <> <h3>Counter in Class Component:</h3> <p>Counter: <b>{this.props.counter}</b></p> <button onClick={() => this.props.onIncrement()}>+</button> <button onClick={() => this.props.onDecrement()}>-</button> </>) } } const mapStateToProps = (state) => { return { counter: state.counter } } const mapDispatchToProps = (dispatch) => { return { onIncrement: () => dispatch({type: actionTypes.INCREMENT}), onDecrement: () => dispatch({type: actionTypes.DECREMENT}) } } export default connect(mapStateToProps, mapDispatchToProps)(CounterClass)
استفاده از ریداکس در فانکشنال کامپوننت CounterFunction:
در بخش قبل با استفاده از دو متد mapStateToProps و mapDispatchToProps توانستیم ارتباط بین store و component را برقرار کنیم. در فانکشنال کامپوننت ها، کار ما کمی آسان تر است و به کمک هوک های useDispatch و useSelector این ارتباط را انجام می دهیم.
در ابتدای کامپوننت CounterFunction دو دستور import زیر را تعریف کنید:
import { useSelector, useDispatch } from "react-redux"; import * as actionTypes from "./actions";
سپس بصورت زیر از دو هوک useSelector و useDispatch استفاده کنید:
const counter = useSelector((state) => state.counter); const dispatch = useDispatch();
حالا می توان از مقدار counter استفاده کرد:
<p> Counter: <b>{counter}</b> </p>
دکمه های افزایش/کاهش یکواحدی شمارنده را بصورت زیر تعریف کنید:
<button onClick={() => dispatch({ type: actionTypes.INCREMENT })}>+1</button> <button onClick={() => dispatch({ type: actionTypes.DECREMENT })}>-1</button>
مشاهده می شود که برای رویداد onClick دکمه ها، فقط کافیست action-type مربوطه را dispatch کنیم.
و دکمه های افزایش/کاهش 5 واحدی شمارنده را بصورت زیر تعریف کنید:
<button onClick={() => dispatch({ type: actionTypes.ADD5, payload: 5 })}>+5</button> <button onClick={() => dispatch({ type: actionTypes.SUB5, payload: 5 })}>-5</button>
برای رویداد onClick دکمه های افزایش/کاهش 5 واحدی، از payload اکشن استفاده کرده ایم.
سورس کد کلی کامپوننت CounterFunction بصورت زیر است:
import { useSelector, useDispatch } from "react-redux"; import * as actionTypes from "./actions"; const CounterFunction = () => { const counter = useSelector((state) => state.counter); const dispatch = useDispatch(); return ( <> <h3>Counter in Functional Component</h3> <p> Counter: <b>{counter}</b> </p> <button onClick={() => dispatch({ type: actionTypes.INCREMENT })}> +1 </button> <button onClick={() => dispatch({ type: actionTypes.DECREMENT })}> -1 </button> <button onClick={() => dispatch({ type: actionTypes.ADD5, payload: 5 })} > +5 </button> <button onClick={() => dispatch({ type: actionTypes.SUB5, payload: 5 })} > -5 </button> </> ); }; export default CounterFunction;
در این مقاله آموزشی نحوه کار با ریداکس را در دو حالت Functional و Class با یک مثال بررسی کردیم. بطور خلاصه داریم:
- کلاس کامپوننت: به منظور استفاده از ریداکس، با متدهای mapStateToProps و mapDispatchToProps و connect سر و کار داریم.
- فانکشن کامپوننت: به منظور استفاده از ریداکس، با هوک های useSelector و useDispatch سر و کار داریم.
دوستان عزیز در انتهای این مقاله، اگر سوال یا ابهامی در زمینه ریداکس (Redux) داشتید لطفا در انتهای همین مقاله، سوال خود را با ما در میان بگذارید.
دیدگاهتان را بنویسید