در پست قبلی، ما به طور کامل محیط Redux را راه اندازی کرده و نمونه Axios را برای ارسال درخواستهای HTTP ایجاد کردیم. در ادامه، در این پست، ما قصد داریم Redux repository را داخل یک component ثبت کرده و از آن برای واکشی داده ها از سرور استفاده کنیم. علاوه بر این، ما میخواهیم component خود را به صورت lazy بارگذاری کنیم تا مزیت بارگذاری محتوا به طور lazy را نشان دهیم.
این مقاله، قسمتی از مجموعه آموزشی زیر میباشد:
- آماده سازی پروژه و ایجاد component ها
- Navigation و مسیریابی (Routing)
- Axios ،HTTP و Redux
- Lazy Loading و HOC Component
- مدیریت خطا و component های اضافی
- ایجاد form داینامیک و component های modal
- اعتبارسنجی Form و Handle کردن درخواست Post
- مدیریت درخواست PUT
- مدیریت درخواست DELETE
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی NET Core. را ببینید، به این لینک مراجعه کنید: دستورالعملهای کار با NET Core.
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی React را ببینید، به این لینک مراجعه کنید: مقدمه معرفی آموزش سریالی React
برای بررسی قسمت قبل، به این لینک مراجعه کنید: HTTP, Axios, Redux
جهت دانلود سورس بر روی این لینک کلیک کنید: سری آموزشی React – قسمت 4
این پست به قسمتهای زیر تقسیم میشود:
- ایجاد کامپوننت HOC،OwnerList و Route ها
- پیاده سازی Redux
- ایجاد کامپوننت Owner
- نمایش نتایج Owners
- بارگزاری محتوا به صورت Lazy
- نتیجه گیری
ایجاد کامپوننت HOC ،OwnerList و Route ها
اجازه دهید ساختار پوشه ای زیر و فایل OwnerList.js را داخل پوشه containers
ایجاد کنیم:

قبل از اصلاح کامپوننت OwnerList، بیایید یک higher-order component (HOC) (کامپوننت با order بالاتر) ایجاد کنیم. ما میخواهیم از آن در یک component، به عنوان یک helper برای wrap کردن محتوای خود داخل بلاک ()return استفاده کنیم. همانطور که (از پستهای قبلی) میدونید، بلاک ()return به یک تگ root و تمام محتوای داخل آن نیاز دارد. اگر نمیخواهیم از یک تگ div یا p که میتواند استایلهای ما را خراب کند استفاده کنیم، میتوانیم این component کمکی را ایجاد کنیم. این component یک wrapper ایجاد میکند که تمام محتوای children را بدون به هم ریختگی استایلهای یک پروژه برمیگرداند.
بنابراین داخل پوشه src
، یک پوشه جدید ایجاد کنیم و نام آن را hoc بگذاریم. در این پوشه، میخواهیم یک پوشه جدید ایجاد کنیم و نام آن را Auxiliary بگذاریم. در آخر یک فایل جدید به نام Auxiliary.js ایجاد کنیم:

حالا باید فایل Auxiliary.js را اصلاح کنیم:
1 2 3 |
const auxiliary = (props) => props.children; export default auxiliary; |
تمام کاری که ما داریم اینجا انجام میدهیم فقط برگرداندن محتوای children است که بین تگ باز و بسته auxiliary قرار میگیرد.
حالا میتوانیم فایل OwnerList.js را اصلاح کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import React, { Component } from 'react'; import { Table, Col, Row } from 'react-bootstrap'; import { Link } from 'react-router-dom'; import Aux from '../../../hoc/Auxiliary/Auxiliary'; class OwnerList extends Component { render() { let owners = []; return ( <Aux> <Row> <Col mdOffset={10} md={2}> <Link to='/createOwner' >Create Owner</Link> </Col> </Row> <br /> <Row> <Col md={12}> <Table responsive striped> <thead> <tr> <th>Name</th> <th>Date of birth</th> <th>Address</th> <th>Details</th> <th>Update</th> <th>Delete</th> </tr> </thead> <tbody> {owners} </tbody> </Table> </Col> </Row> </Aux> ) } } export default OwnerList; |
شرح کامپوننت OwnerList
منطق پشت این کد کاملا ساده است.
ما اینجا یک آرایه خالی به نام owners ایجاد کرده ایم که قرار است تمام owner های درون دیتابیس را در خود نگه دارد. بالای جدول، یک link به کامپوننت Create وجود دارد. ما اینجا داریم از کامپوننت Link از کتابخانه react-router-dom استفاده میکنیم. سپس باید یک جدول ایجاد کنیم که تمام داده های درون آرایه را نشان دهد، اگرچه در حال حاضر، آن فقط یک آرایه خالی میباشد.
حالا بیایید فایل App.js را برای اضافه کردن یک route به این component اصلاح کنیم:
1 |
import OwnerList from './Owner/OwnerList/OwnerList'; |
1 2 3 |
<Route path="/" exact component={Home} /> <Route path="/owner-list" component={OwnerList} /> <Route path="*" component={NotFound} /> |
اگر اپلیکیشن خود را با دستور npm start اجرا کنیم، در نتیجه، میتوانیم با کلیک کردن روی منوی Owner Actions به کامپوننت OwnerList برویم:

پیاده سازی Redux
از آنجایی که در حال حاضر، هیچ داده ای نداریم، اجازه دهید مقداری داده اضافه کنیم.
اول باید کامپوننت خود را به reducer خود متصل کنیم. reducer قرار است state را تنظیم کند و داده ها را به عنوان یک ویژگی در داخل آبجکت props به این component منتقل کند.
برای این منظور، connect
را از کتابخانه react-redux وتمام action ها را از فایل repositoryAction.js ایمپورت کنیم:
1 2 |
import { connect } from 'react-redux'; import * as repositoryActions from '../../../store/actions/repositoryActions'; |
سپس زیر براکت بسته کامپوننت خود و درست بالای دستور export، باید function های زیر را اضافه کنیم و سپس دستور export را تغییر دهیم:
1 2 3 4 5 6 7 8 9 10 11 |
const mapStateToProps = (state) => { return { data: state.data } } const mapDispatchToProps = (dispatch) => { return { onGetData: (url, props) => dispatch(repositoryActions.getData(url, props)) } } export default connect(mapStateToProps, mapDispatchToProps)(OwnerList); |
تابع mapStateToProps
ویژگی data
را از آبجکت initialState
از repositoryReducer به ویژگی data داخل کامپوننت OwnerList مپ میکند. برای دسترسی به این ویژگی data
، فقط باید آن را به صورت this.props.data فراخوانی کنیم.
تابع mapDispatchToProps
ویژگی اضافی onGetData را ایجاد میکند. ما ممکن است آن را با دستور this.props.onGetData فراخوانی کنیم. سپس این ویژگی، اکشن مورد نظر داخل فایل repositoryActions.js را dispatch میکند که این اکشن قرار است داده ها را از سرور واکشی کند.
جمع بندی Redux
در حال حاضر اگر به نمودار پست قبلی نگاه کنیم، همه چیز کاملاً منطقی است. از این component، ما action داخل فایل
repositoryActions.js
را فراخوانی میکنیم. این action داده ها را از سرور واکشی کرده و reducer را trigger میکند. reducer ما با تغییر ویژگی data داخل آبجکتstate
،initialState
را آپدیت میکند. در آخر، Central Store، این ویژگی data را با تابعmapStateToProps
به این component مپ میکند.
ایجاد کامپوننت Owner
چون ما میخواهیم چند owner را داخل کامپوننت OwnerList نمایش دهیم، بیایید کامپوننت Owner را ایجاد کرده و آن را به داخل کامپوننت OwnerList ایمپورت کنیم.
در پوشه components ساختار زیر را ایجاد کنید:

برای کار با تاریخها، باید یک کتابخانه third-party دیگر به نام react-moment را نصب کنیم:
1 |
npm install --save react-moment |

کتابخانه moment را نیز باید نصب کنیم:
1 |
npm install --save moment |
حالا میتوانیم فایل Owner.js را اصلاح کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import React from 'react'; import Aux from '../../../hoc/Auxiliary/Auxiliary'; import Moment from 'react-moment'; import { Button } from 'react-bootstrap'; const redirectToOwnerDetails = (id, history) => { history.push('/ownerDetails/' + id); } const redirectToUpdateOwner = (id, history) => { history.push('/updateOwner/' + id); } const rediterctToDeleteOwner = (id, history) => { history.push('/deleteOwner/' + id); } const owner = (props) => { return ( <Aux> <tr> <td>{props.owner.name}</td> <td><Moment format="DD/MM/YYYY">{props.owner.dateOfBirth}</Moment></td> <td>{props.owner.address}</td> <td> <Button onClick={() => redirectToOwnerDetails(props.owner.id, props.history)}>Details</Button> </td> <td> <Button bsStyle="success" onClick={() => redirectToUpdateOwner(props.owner.id, props.history)}>Update</Button> </td> <td> <Button bsStyle="danger" onClick={() => rediterctToDeleteOwner(props.owner.id, props.history)}>Delete</Button> </td> </tr> </Aux> ) } export default owner; |
در این child component، ما داده های owner را از طریق آبجکت props
دریافت میکنیم. سپس یک row به همراه داده ها و چند دکمه برای رفتن به component های مختلف ایجاد میکنیم. همه دکمه ها، تابع های ارجاعی هستند که redirect به سمت کامپوننتهای details، update و delete را امکان پذیر میکنند.
نکته مهمی که باید به آن توجه کرد این است که آبجکت props دارای ویژگی “history” است که به ما این امکان را می دهد که به صورت برنامه ریزی شده navigate کنیم. علاوه بر این، ما اینجا از قالب تاریخ استفاده کرده ایم: “DD/MM/YYYY” فقط برای اینکه نشان دهیم کار با فرمت ها با استفاده از کتابخانه Moment چقدر آسان است. برای کامپوننتهای create و update، از قالب “MM/DD/YYYY
” استفاده خواهیم کرد. تنها کاری که باید انجام دهید این است که این کامپوننت را در کامپوننت OwnerList ایمپورت کنید و تمام owner ها را روی صفحه نمایش دهید.
نمایش نتایج Owners
کامپوننت Owner را داخل کامپوننت OwnerList ایمپورت کنید:
1 |
import Owner from '../../../components/OwnerComponents/Owner/Owner'; |
سپس بالای قسمت ()render، قصد داریم یک function جدید ایجاد کنیم. با این function، قصد داریم ویژگی OnGetData
را برای واکشی داده ها از سرور فراخوانی کنیم:
1 2 3 4 |
componentDidMount = () => { let url = '/api/owner'; this.props.onGetData(url, { ...this.props }); } |
componentDidMount
یک lifecycle hook است و به محض رندر شدن component فراخوانی میشود.
در آخر، داخل render function، کد زیر را برای پر کردن آرایه “owners
” خود اضافه کنید:
1 2 3 4 5 6 7 8 |
let owners = []; if (this.props.data && this.props.data.length > 0) { owners = this.props.data.map((owner) => { return ( <Owner key={owner.id} owner={owner} {...this.props} /> ) }) } |
در این function، بررسی میکنیم که آیا ویژگی data پر شده است یا خیر (زیرا درخواستهای http، درخواستهای async هستند) و اینکه آیا یک آرایه است یا خیر. سپس برای هریک از عنصرهای این آرایه، ما کامپوننت Owner را با داده ها پر میکنیم. به ویژگی key که هنگام ایجاد component های فرزند از آرایه آبجکتها الزامی است توجه کنید. علاوه بر این، آبجکت props را پاس می دهیم تا به ویژگی “history” آن آبجکت دسترسی داشته باشیم. به همین ترتیب، یک آبجکت واحد owner را به child component میفرستیم زیرا میخواهیم این child component بتواند از دستور props.owner استفاده کند.
عالی.
حالا اگر بخواهیم به منوی Owner Actions برویم، نتیجه را با owner های زیر خواهیم دید:

بارگزاری محتوا به صورت Lazy
تا به حال، ما کامپوننت OwnerList خود را بلافاصله بارگذاری می کردیم و نه به صورت تنبل (lazy)، به این معنی که پس از شروع برنامه، همه منابع نیز بلافاصله بارگیری می شوند. این بهترین شیوه نیست زیرا ممکن است کاربر هرگز از صفحه Owner Actions بازدید نکند، بنابراین منابع این صفحه نیز نباید بارگیری شوند.
بیایید پروژه خود را اصلاح کنیم تا بتوانیم از ویژگی بارگذاری تنبل (lazy loading) استفاده کنیم.
بیایید در داخل پوشه hoc، یک پوشه جدید به نام AsyncComponent و داخل این پوشه، یک فایل جدید به نام AsyncComponent.js ایجاد کنیم و آن را تغییر دهیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import React, {Component} from 'react'; const asyncComponent = (importComponent) => { return class extends Component{ state = { component: null } componentDidMount(){ importComponent() .then(cmp => { this.setState({component: cmp.default}); }); } render(){ const C = this.state.component; return C ? <C {...this.props} /> : null; } } } export default asyncComponent; |
با این کامپوننت، کامپوننت خود را به صورت ناهمگام (async) بارگذاری می کنیم.
برای کامل کردن این action، ما باید فایل App.js را برای بارگزاری کامپوننت OwnerList خود در async mode اصلاح کنیم:
1 2 |
//import OwnerList from './Owner/OwnerList/OwnerList'; import asyncComponent from '../hoc/AsyncComponent/AsyncComponent'; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const AsyncOwnerList = asyncComponent(() => { return import('./Owner/OwnerList/OwnerList'); }); class App extends Component { render() { return ( <BrowserRouter> <Layout> <Switch> <Route path="/" exact component={Home} /> <Route path="/owner-list" component={AsyncOwnerList} /> <Route pat="*" component={NotFound} /> </Switch> </Layout> </BrowserRouter> ); } } export default App; |
حالا اینجا ما کامپوننت Async خود را import میکنیم و داخل آن، کامپوننت OwnerList خود را import میکنیم. در آخر، ما OwnerList را دیگر داخل کامپوننت Route بارگزاری نمیکنیم، در صورتی که به جای آن، AsyncOwnerList را بارگزاری میکنیم.
اکنون اگر پس از restart کردن برنامه خود به صفحه Owner Actions بروید، فایل اضافی دیگری به نام chunk را مشاهده خواهید کرد که فقط برای این صفحه بارگذاری شده است.

نتیجه گیری
با خواندن این پست، شما موارد زیررا یاد گرفتید:
- شیوه ایجاد کامپوننتهای HOC
- نحوه پیاده سازی Redux برای واکشی داده ها از سرور
- نحوه استفاده از کتابخانه Moment برای قالب بندی تاریخها
- شیوه بارگزاری component ها به شیوه async با استفاده از ویژگی Lazy Loading
بابت خواندن این مقاله از شما تشکر میکنیم و امیدواریم که برای شما مفید واقع قرار گرفته باشد.
در قسمت بعدی این سری از آموزش، قصد داریم نحوه handle کردن خطاها در حین ارسال درخواستهای HTTP را یاد بگیریم.