شروع کار با ریکت (react.js) – یک مرور کلی و آموزش پیاده سازی

از زمانی که برنامه نویسی با جاوا اسکریپت رو شروع کردم درباره ریکت میشنیدم، اما اعتراف میکنم که یک نگاهی به اون انداختم و منو واقعا ترسوند! من دسته ای از کدهای Html که با جاوا اسکریپت ترکیب شده بودن رو دیدم و فکر میکنم این چیزی نیست که ما سعی کنیم ازش دوری کنیم. معامله بزرگ با React چیست؟

در عوض من فقط بر روی یادگیری وانیلا جاوا اسکریپت و کار با جی کوئری به صورت حرفه ای متمرکز شدم. بعد از چند بار ناامیدی و تلاش های ناموفق که برای شروع ریکت بود، بالاخره شروع به یادگیری اون کردم و فهمیدم که چرا ممکنه بخوام از ریکت بجای Vanilla Js یا jQuery استفاده کنم!

من سعی کردم مواردی رو که یاد گرفتم بصورت فشرده در یک مقدمه خوب با شما به اشتراک بزارم، پس با من همراه باش 🙂

پیش نیاز هامون چیه؟!

مواردی هست که شما باید قبل از شروع به کار با ریکت بدونی. حالا اگر تا بحال از جاوا اسکریپت یا DOM اصلا استفاده نکردی! اشکالی نداره 🙂 من شما رو با این موارد قبل از اینکه با ریکت کار کنی آشنا میکنم.

در لیست زیر مواردی که پیش نیاز های ریکت هست رو آوردم:

  • آشنایی خوبی با Html و Css داشته باشیم.
  • یک دانش عمومی از برنامه نویسی و زبان جاوا اسکریپت رو داشته باشیم.
  • به خوبی DOM (Document Object Model) رو فهمیده باشیم.
  • با دستورات و ویژگی های اکما اسکریپت 6 (ES)6 آشنایی داشته باشیم.
  • و در آخر Node.js و npm رو نصب کرده باشیم.

اهدافمون چیه؟!

  1. یادگرفتن بعضی از اصطلاحات و مفاهیم مهم در ریکت مثل Bable, Webpack, JSX, Components, Props, State و lifeycycle
  2. یک برنامه خیلی آسون در ریکت باهم درست میکنیم که مفاهیمی که گفتم رو نشون بده.

در اینجا سورس کدها و پیش نمایش از نتیجه برنامه مون داریم:

ریکت (React) چیست؟

ریکت یکی از محبوب ترین کتابخانه های جاوا اسکریپت با بیش از 100 هزار ستاره در گیت هاب هست.

ریکت یک فریم ورک نیست! (برخلاف Angular)

ریکت یک پروژه اُپن سورس هست که سازنده اون فیسبوکه.

ریکت برای ساخت رابط کاربری (Ui) در فرانت اِند استفاده میشه.

ریکت یک لایه View از مدل MVC(Model View Controller) هست.

یکی از مهمترین مزیت های React اینه که شما میتونی کامپوننت بسازی، که مثل المان های Html قابل شخصی سازی و استفاده مجدد در برنامه هست. و این مزیت باعث ایجاد سریع و کارآمد رابط کاربری برنامه میشه. ریکت همچنین ذخیره سازی و مدیریت داده های برنامه رو با استفاده از State و Props ساده تر کرده.

خب حالا میخایم مواردی رو که راجبش صحبت کردیم رو انجام بدیم، بریم که شروعش کنیم 🙂

نصب و راه اندازی

راه های زیادی برای راه اندازی ریکت وجود داره که من اینجا دو راه رو به شما نشون میدم که ببینی چطوری کار میکنه.

فایل HTML استاتیک

اولین روش، روش رایجی برای راه اندازی ریکت نیست و ما نمیخایم بقیه آموزش ها رو اینطوری انجام بدیم، اما اگه تا حالا از کتابخانه ها مثل جی کوئری استفاده کردین فهمیدن این روش براتون آشنا و آسونه. و این حداقل راه ترسناک برای شروع هست اگر شما با webpack, bable و node.js آشنا نیستین.

خب برای شروع یک فایل index.html میسازیم و سه تا cdn داخل تگ head که react, react DOM و babel هست رو لود میکنیم. همچنین ما یک div درست میکنیم و اسم آیدی اون رو root میزاریم و در نهایت یک تگ script قرار میدیم.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  </head>

  <body>
    <div id="root"></div>

    <script type="text/babel">
      // React code will go here
    </script>
  </body>
</html>
  • React – شامل api های سطح بالای ریکت
  • React DOM – متد های خاص DOM رو اضافه میکنه
  • Bable – یک کامپایلر جاوا اسکریپته که به ما اجازه میده از ES6 در مرورگر های قدیمی استفاده کنیم

نقطه شروع برنامه ما div ای است که به آن آیدی root دادیم.

حالا اولین قطعه کد ریکت رو مینویسیم، ما استفاده میکنیم از کلاس های ES6 برای ساخت یک کامپوننت ریکت که آن را App می نامیم.

class App extends React.Component {
  //...
}

حالا تابع render() رو اضافه میکنیم، این تابع تنها تابعی هست که در کلاس کامپوننت ضروری است که که برای رندر کردن DOM nodes استفاده میشه.

class App extends React.Component {
  render() {
      return (
          //...
      );
  }
}

داخل return ، یک کد html ساده قرار میدیم. یادتون باشه که ما نمیخایم یک رشته رو برگردونیم، پس از “” استفاده نمیکنیم. این رو بهش JSX میگن که بزودی یادش میگیریم.

class App extends React.Component {
  render() {
    return <h1>Hello world!</h1>
  }
}

در نهایت ما استفاده میکنیم از تابع render() در ReactDom تا کلاس App ما رندر و در دایو root فایل html ساخته بشه.

ReactDOM.render(<App />, document.getElementById('root'))

و در اینجا کل کدامون رو داریم:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  </head>

  <body>
    <div id="root"></div>

    <script type="text/babel">
      class App extends React.Component {
        render() {
          return <h1>Hello world!</h1>
        }
      }

      ReactDOM.render(<App />, document.getElementById('root'))
    </script>
  </body>
</html>

و حالا اگر فایل index.html رو در مرورگر ببینی، می بینی که ما یک تگ h1 رندر شده در DOM ساختیم.

بسیار خوب دیدی که ریکت ترس نداشت 🙂

خب حالا میریم سراغ روش بعدی:

ساخت برنامه ریکت

در این روش من کتابخانه های جاوا اسکریپت رو در فایل html لود میکنم و react و babel خیلی کارآمد نیست و نگهداریش سخت هست.

خوشبختانه، فیسبوک Create React App رو ساخت، محیطی که هر چیزی که شما برای ساخت برنامه ریکت نیاز داشته باشید رو به طور پیش فرض دارد. این یک سرور توسعه لایو میسازه با استفاده از webpack بصورت خودکار ریکت و jsx رو کامپایل میکنه و از ESlint برای تست و اخطار اشتباهات استفاده میکنه.

برای نصب، کد creat-react-app در ترمینال اجرا کنید و مطمئن شوید که از node.js با ورژن 5.2 یا بالاتر استفاده میکنید.

npx create-react-app react-tutorial

خب نصب و راه اندازی انجام شد، حالا میریم داخل پوشه ای که ساخته شده و پروژه را شروع میکنیم.

cd react-tutorial
npm start

هنگامی که این دستور رو اجرا کنیم، صفحه ای از مرورگر با آدرس localhost:3000 باز میشود.

اگر میخای ساختار پروژه رو ببینی، باید وارد پوشه های /public و /src بشی که در کنار اونها node_modules , gitignore , README.md و package.json هم هست.

در داخل پوشه /public فایل مهم ما index.html هست که خیلی شبیه به فایل استاتیک index.html هست که یکم قبلتر ساختیتمش که فقط یک دایو root داشت. این بار، هیچ کتابخانه و اسکریپتی داخل فایل لود نشده! و پوشه /src شامل همه ی کد های react ما میشه.

می بینید که این محیط بصورت بصورت خودکار کد های ریکت ما رو کامپایل و آپدیت میکنه، خط زیر رو در ادرس /src/App.js پیدا کنید:

To get started, edit `src/App.js` and save to reload.

و اون رو با هر متن دلخواهی جایگزین کنید. بعد از ذخیره فایل، شما متوجه میشی که localhost:3000 با داده های جدید کامپایل و آپدیت شده.

خب میریم جلوتر و همه فایل های داخل src/ رو حذف میکنیم ، و فقط فایل های index.css و index.js رو نگه میداریم.

برای index.css، من همه کد های primitive css رو داخل فایلم کپی و پیست کردم ولی شما میتونید از بوت استرپ یا سایر فریم ورک های css استفاده کنید یا اصلا میتونید استفاده نکنید! اینکار باعث میشه آسونتر کد بزنم.

حالا میریم داخل فایل index.js و React , ReactDOM و فایل css رو اضافه میکنیم:

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'

حالا کامپوننت App رو دوباره میسازیم، در روش قبلی ما فقط یک تگ h1 داشتیم اما الان این تگ رو داخل یک div با یک کلاس قرار میدیم. یادتون بمونه که ما از className بجای class استفاده می کنیم. این اولین نکته برای کد زدن داخل جاوا اسکریپت هست.

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

و در اخر این قطعه کد رو برای برای رندر کردن root در App مینویسیم:

ReactDOM.render(<App />, document.getElementById('root'))

و در اینجا کد کامل فایل index.js رو داریم. این بار، ما Component رو بعنوان یک صفت (property) از ریکت لود می کنیم، بنابراین ما نیازی نداریم از React.Component گسترشش (extend) بدیم.(منظورش روش قبلیه)

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

حالا اگر برگردیم به localhost:3000 یک “Hello, React!” مثل روش قبلی می بینیم. و ما حالا یک برنامه react داریم!

ابزار توسعه ریکت (React Developer Tools)

یک extension بنام react developer tools برای ریکت داریم که استفاده از اون کار با ریکت رو خیلی آسونتر میکنه، میتونید از لینک: react developer tools برای مرورگر کروم دانلودش کنین، یا برای هر مرورگری که ترجیح میدین ازش استفاده کنین.

بعد از نصب این افزونه، وقتی که devtools رو باز کنی یک تب با نام react می بینی. روش کلیک کن، حالا از اینجا میتونی کامپوننت هایی که نوشتی رو بررسی کنی. و میتونی بری به تب Elements تا خروجی واقعی DOM رو ببینی. ولی خب بنظر میرسه الان چندان مفید نباشه، اما وقتی که برنامه کامل و پیچیده شد به طور وحشتناکی استفاده کردن ازش مهم میشه 😐

حالا ما تمامی ابزار و نصب رو کامل کردیم برای شروع کار با ریکت 🙂

JSX : Javascript + Xml

همونطوری که دیدیم ما از چیزی مثل HTML داخل کدهای ریکت مون استفاده کردیم، اما این کاملا یک HTML نیست. این JSX هست که برای Javascript XML استفاده میشه.

به وسیله JSX ما میتونیم چیزی شبیه HTML بنویسیم و همچنین ما میتونیم از تگ های مشابه XML استفاده کنیم. اینجا می بینیم که JSX به یک متغییر اختصاص داده شده!

const heading = <h1 className="site-heading">Hello, React</h1>

استفاده از JSX برای نوشتن ریکت اجباری نیست. createElement که در حال اجراست که تگ رو میگیره، این آبجکت شامل ویژگی ها (properties) و فرزندان کامپوننت و ارائه اطلاعات مشابه هست. در کد زیر ما یک خروجی مشابه jsx که در بالا تر گفتیم داریم.

const heading = React.createElement('h1', { className: 'site-heading' }, 'Hello, React!')

jsx در واقع به زبان جاوا اسکریپت نزدیکتره تا HTML، بنابراین چند تفاوت اصلی وقتی که می نویسیمش وجود داره

  • className بجای class که در css استفاده میکردیم، استفاده میشه، چون class در جاوا اسکریپت یک کلمه کلیدی رزرو شده هست.
  • ویژگی ها و روش ها در jsx به صورت camelCase هستند (برای مثال onclick رو می نویسیم onClick)
  • تگ ها توسط خودشون بسته میشند (برای مثال <img /<)

عبارات جاوا اسکریپت در داخل JSX با استفاده از آکولاد {} میتونه جاسازی بشه و متغییرها، توابع و ویژگی ها رو شامل بشه.

const name = 'Tania'
const heading = <h1>Hello, {name}</h1>

jsx خیلی آسونتره برای نوشتن و فهمیدن نسبت به اینکه ما بیایم المان هایی رو در وانیلا جاوا اسکریپت بسازیم و اضافه کنیم، و این یکی از دلایلی هست که برنامه نویسان عاشق React هستن 🙂

کامپوننت ها (Components)

تا اینجای کار ما یک کامپوننت به نام App ساختیم. تقریبا همه چیز در ریکت شامل کامپوننت ها هست که میتونه class components یا simple components باشه.

اکثر برنامه های ریکت شامل کامپوننت های کوچکی هستن، و همه چی داخل کامپوننت App لود میشه. کامپوننت های اغلب فایل خودشون رو دریافت میکنن، پس بیاین برای انجام این کار پروژه رو تغییر بدیم.

کلاس App رو از فایل index.js پاک کنین، بنابراین باید شبیه کد زیر بشه:

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'

ReactDOM.render(<App />, document.getElementById('root'))

حالا ما یک فایل بنام App.js میسازیم و کامپوننت رو در اون قرار میدیم.

import React, { Component } from 'react'

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

export default App

خب ما اومدیم کامپوننت App رو export کردیم و داخل فایل index.js لودش کردیم. جدا کردن کامپوننت یک کار اجباری نیست! اما اگه اینکارو انجام ندیم شروع بدی برای برناممون خواهیم داشت که بزودی کنترلش از دستمون خارج میشه.

کلاس های کامپوننت (Class Components)

خب حالا یک کامپوننت دیگه درست میکنیم. ما میخوایم کی جدول بسازیم. پس Table.js رو درست میکنیم و داخلش رو با کدهای زیر پر می کنیم:

import React, { Component } from 'react'

class Table extends Component {
  render() {
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Job</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Charlie</td>
            <td>Janitor</td>
          </tr>
          <tr>
            <td>Mac</td>
            <td>Bouncer</td>
          </tr>
          <tr>
            <td>Dee</td>
            <td>Aspiring actress</td>
          </tr>
          <tr>
            <td>Dennis</td>
            <td>Bartender</td>
          </tr>
        </tbody>
      </table>
    )
  }
}

export default Table

این کامپوننت یک کلاس کامپوننت دلخواه هست. ما کاری کردیم که این کامپوننت با یک المان HTML معمولی، متفاوت باشه. به App.js برمیگردیم، حالا Table رو میتونیم داخلش لود کنیم که ابتدا طبق کد زیر اضافه میکنیم:

import Table from './Table'

حالا میایم و داخل تابع render() در کامپوننت App لودش میکنیم جایی که قبلا “Hello, React!” داشتیم. من className دایو (div) رو به container تغییر میدم.

return (
  <div className="container">
    <Table />
  </div>
)

حالا اگه برگردیم به صفحه مرورگرمون، می بینیم که جدول ( Table) اضافه شده.

حالا یک کلاس کامپوننت دلخواه رو می بینیم. ما بارها و بارها میتونیم ازش استفاده مجدد بکنیم. با این حال داده های که داخل جدول هست به صورت hard code هست و این بنظر میرسه که مفید نیست!

کامپوننت ساده (Simple Component)

یک نوع دیگر کامپوننت در ریکت Simple Component هست، که یک تابع هست. این کامپوننت از کلمه کلیدی class استفاده نمیکنه. حالا بیاین جدول مونو برداریم و دو تا Simple Component درست کنیم – یکی برای سربرگ جدول و یکی برای بدنه جدول.

ما قصد داریم از arrow functions در ES6 برای ساخت Simple Component استفاده کنیم. ابتدا سربرگ جدول :

const TableHeader = () => {
  return (
    <thead>
      <tr>
        <th>Name</th>
        <th>Job</th>
      </tr>
    </thead>
  )
}

و سپس بدنه جدول:

const TableBody = () => {
  return (
    <tbody>
      <tr>
        <td>Charlie</td>
        <td>Janitor</td>
      </tr>
      <tr>
        <td>Mac</td>
        <td>Bouncer</td>
      </tr>
      <tr>
        <td>Dee</td>
        <td>Aspiring actress</td>
      </tr>
      <tr>
        <td>Dennis</td>
        <td>Bartender</td>
      </tr>
    </tbody>
  )
}

و در آخر کلاس Table بصورت زیر میشه:

class Table extends Component {
  render() {
    return (
      <table>
        <TableHeader />
        <TableBody />
      </table>
    )
  }
}

خب همه چیز همونطوری که قبلا گفته شد انجام میدیم، همونطوری که می بینین، کامپوننت ها میتونن بصورت تودرتو باشن، و کامپوننت class و simple با هم ترکیب بشن.

یک کامپوننت class باید شامل render() باشه، و تابع return فقط میتونه یک عنصر والد رو برگردونه.

به عنوان جمع بندی، بیاین کامپوننت simple و class رو با باهم مقایسه کنیم:

const SimpleComponent = () => {
  return <div>Example</div>
}
class ClassComponent extends Component {
  render() {
    return <div>Example</div>
  }
}

یادمون باشه که وقتی return یک خط داشته باشه، نیازی نیست که از پرانتز ها استفاده کنیم.

Props

همین الان ما یک جدول خُنُک داریم 🙂 که داده های داخلش به صورت hard code وارد شده. یکی از مزیت های بزرگ ریکت مدیریت داده ها هست. و این کار با ویژگی (propetries) های Props و State انجام میشه. خب اول میریم سراغ مدیریت داده هامون با ویژگی Props.

در ابتدا همه داده هامون رو از TableBody حذف میکنیم.

const TableBody = () => {
  return <tbody />
}

سپس همه داده ها رو به آرایه ای از آبجکت های منتقل میکنیم, مثل اینکه ما از JSON-based api استفاده کردیم. ما باید این آرایه رو داخل render() درفایل App.js ایجاد کنیم.

class App extends Component {
  render() {
    const characters = [
      {
        name: 'Charlie',
        job: 'Janitor',
      },
      {
        name: 'Mac',
        job: 'Bouncer',
      },
      {
        name: 'Dee',
        job: 'Aspring actress',
      },
      {
        name: 'Dennis',
        job: 'Bartender',
      },
    ]

    return (
      <div className="container">
        <Table />
      </div>
    )
  }
}

حالا ما قصد داریم داده هامون رو از این طریق به کامپوننت بچه(Table) به همراه ویژگی هاش پاس بدیم، چطوری ممکنه داده هامون رو از طریق استفاده از صفت های data- پاس بدیم. ما میتونیم ویژگی که میخایم رو صدا بزنیم، تا زمانیکه یک کلمه کلیدی رزرو شده نباشه، بنابراین من characterData رو مینویسم. داده ای که من از طریق متغییر characters پاس میدم، و من اونو داخل آکولاد قرار میدم که این یکی از قوانین جاوا اسکریپت هست.

return (
  <div className="container">
    <Table characterData={characters} />
  </div>
)

حالا داده ای که از این طریق به Table پاس دادیم رو از طرف دیگر یعنی در کامپوننت Table که دسترسی داریم باید باهاش کار کنیم.

class Table extends Component {
  render() {
    const { characterData } = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    )
  }
}

اگر react devtools رو در مرورگر باز کنیم و کامپوننت Table رو بررسی کنیم. شما آرایه ای از داده های در این ویژگی میبینید. داده ها در اینجا ذخیره شدند که بهش DOM مجازی میگن، که یک راه سریع و موثر برای همگام سازی داده با DOM واقعی هست.

داده ها در حال حاظر در DOM واقعی نیستن، اگرچه در Table هستن، ما میتونیم دسترسی به همه Props ها از طریق this.props داشته باشیم. ما از این طریق فقط یک props رو پاس میدیم، بله characterData، بنابراین ما از this.props.characterData داده های رو برمی گردونیم.

من قصد دارم از ویژگی shorthand در ES6 برای ساخت یک متغییر که شامل this.props.characterData میشه استفاده کنم.

const { characterData } = this.props

از اونجایی که کامپوننت Table ما در واقع شامل دو تا simple component کوچک میشه، من قصد دارم اونو دوباره از طریق props به TableBody پاس بدم.

class Table extends Component {
  render() {
    const { characterData } = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    )
  }
}

و حالا TableBody هیچ پارامتر و return ای نداره، فقط یک تگ هست.

const TableBody = () => {
  return <tbody />
}

ما قصد داریم props رو بعنوان یک پارامتر پاس بدیم، و map though the array که یک سطر جدول رو از هر آبجکت آرایه برمیگردونه. این map شامل متغییر rows میشه که یک عبارت رو برمیگردونه.

const TableBody = props => {
  const rows = props.characterData.map((row, index) => {
    return (
      <tr key={index}>
        <td>{row.name}</td>
        <td>{row.job}</td>
      </tr>
    )
  })

  return <tbody>{rows}</tbody>
}

حالا اگه بریم و برنامه رو در مرورگر ببینیم، می بینیم که داده هامون لود شده.

به این توجه کنید که من یک ایندکس key به هر سطر جدول دادم، شما بهتره همیشه از keys وقتی چندین لیست در ریکت دارین استفاده کنین، اونا به شناسایی هر آیتم لیست کمک میکنن. ما همچنین دیدیم که اینکار مهمیه برای زمانی که میخایم آیتم های لیست رو تغییر بدیم.

props ها یک راه موثر برای پاس دادن داده های موجود در کامپوننت React هستن. با این حال کامپوننت، props رو نمیتونه تغییر بده چون اونا فقط خواندنی (read-only) هستن. در قسمت بعدی، ما یاد می گیریم که چطور از State برای کنترل بیشتر بر مدیریت داده ها در React استفاده کنیم.

State

همین حالا، ما داده هامون رو در یک آرایه داخل یک متغییر ذخیره کردیم، و اونها رو از طریق props پاس دادیم.این برای شروع خوبه، اما تصور کنید اگه ما بخایم آیتمی رو از آرایه حذف کنیم به وسیله props، ما فقط یک جریان داده داریم( یعنی فقط خواندنی هست) اما به وسیله State ما میتونیم داده های خصوصیمون رو در کامپوننت آپدیت (ویرایش) کنیم.

شما میتونید State رو برای هر داده ای که ذخیره شده و ویرایش شده در نظر بگیرید بدون اینکه لزوما یک دیتابیس (پایگاه داده) اضافه شده باشه – مثل: اضافه کردن و پاک کردن آیتم های داخل سبد خرید قبل از اینکه خرید تکمیل بشه.

برای شروع، ما قصد داریم یک آبجکت State بسازیم:

class App extends Component {
  state = {}
}

این آبجکت شامل شامل ویژگی هایی برای هر چیزی که شما میخاین در state ذخیره کنید هست. برای ما characters هست.

class App extends Component {
  state = {
    characters: [],
  }
}

همه آرایه های آبجکت رو که قبلا ساخیم به داخل state.characters انتقال میدیم.

class App extends Component {
  state = {
    characters: [
      {
        name: 'Charlie',
        // the rest of the data
      },
    ],
  }
}

داده های ما به صورت رسمی در state قرار میگیرد. از اونجایی که ما میخایم یک کاراکتر رو از جدول حذف کنیم، ما قصد داریم یک تابع removeCharacte در کلاس والد App بسازیم.

برای برگردوندن state، ما از this.state.charactes میگیریمش، و مثل قبل با استفاده از تابعی مشابه در ES6. برای آپدیت(ویرایش) کردن State، ما از this.setState() استفاده میکنیم، که یک تابع ساخته شده برای ویرایش state است. ما آرایه رو برا اساس index فیلتر میکنیم و از همون طریق پاسش میدیم، و آرایه ای جدید برمیگردونیم.

شما باید از this.setState() برای ویرایش یک آرایه استفاده کنید. و درخواست یک مقدار جدید با this.state.property امکان پذیر نیست.

removeCharacter = index => {
  const { characters } = this.state

  this.setState({
    characters: characters.filter((character, i) => {
      return i !== index
    }),
  })
}

filter آرایه رو تغییر نمیده بلکه یک آرایه جدید درست میکنه، و این یک تابع خوب و پیشنهاد شده برای ویرایش کردن آرایه ها در جاوا اسکریپت هست. این یک تابع خاص برای تست کردن یک ایندکس با تمامی ایندکس های دیگه ای که در آرایه هست، است :/ و همه ی داده ها رو برمیگردونه بجز داده ای که از اون طریق پاس داده بشه!

حالا ما باید این تابع رو از طریق کامپوننت پاس بدیم. و یک دکمه رو برای هر کاراکتری که توسط تابع فراخوانی میشه رندر کنیم. ما تابع removeCharacter رو از طریق یک props به Table پاس میدیم.

return (
  <div className="container">
    <Table characterData={characters} removeCharacter={this.removeCharacter} />
  </div>
)

فراموش نکنید که const {characters} = this.state رو برای جمع آوری داده ها از state قرار بدین.

از اونجایی که ما داده ها رو به TableBody در Table پاس دادیم. ما باید اونو دوباره از طریق یک props پاس بدیم، مثل همون کاری که با character data کردیم.

class Table extends Component {
  render() {
    const { characterData, removeCharacter } = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} removeCharacter={removeCharacter} />
      </table>
    )
  }
}

در اینجا ایندکسی که در تابع removeCharacter() تعریف کردیم رو میاریم. در کامپوننت TableBody ، ما پاس میدیم یک ایندکس/کلید رو از طریق یک پارامتر، بنابراین تابع filter میدونه که کدوم آیتم رو حذف کنه. ما یک دکمه با onClick ساختیم و اون رو از این طریق پاس میدیم.

<tr key={index}>
  <td>{row.name}</td>
  <td>{row.job}</td>
  <td>
    <button onClick={() => props.removeCharacter(index)}>Delete</button>
  </td>
</tr>

تابع onClick رو باید پاس بدیم به تابعی که تابع removeCharacter() برمیگردونه، در غیر اینصورت سعی میکنه که به صورت خودکار اجرا بشه.

عالیه! حالا ما یک دکمه حذف داریم، و ما میتونیم state مون رو ویرایش کنیم.

من Mac رو پاک کردم.

حالا شما باید میدونید که State چطوری مقدار دهی اولیه میشه و چطوری میتونه ویرایش بکنه.

ارسال داده فرم

حالا ما داده هامون رو در state ذخیره کردیم، و ما میتونیم هر آیتمی رو از state پاک کنیم. با این حال، اگر ما میخایم داده ای جدید به state اضافه کنیم چطوری میشه؟ در یک برنامه واقعی، شما احتمالا با یک state خالی شروع میکنید و بهش اضافه میکنید، مثل یک برنامه todo list یا یک سبد خرید.

قبل از هرچیزی، بریم و همه داده های hard code مون رو از state.characters پاک کنیم، و ما اون رو از طریق فرم آپدیت میکنیم.

class App extends Component {
  state = {
    characters: [],
  }
}

حالا بریم جلوتر و یک کامپوننت Form در یک فایل جدید به نام Form.js بسازیم. ما قصد داریم یک class component بسازیم، و بهمراه اون از constructor() استفاده میکنیم، که تا حالا اینکارو نکرده بودیم. ما به constructor() برای استفاده از this نیاز داریم، و همچنین برای دریافت props از والد.

ما قصد داریم state اولیه رو در Form بعنوان یک آبجکت همراه با ویژگی های خالی تنظیم کنیم، و به state اولیه this.state رو اختصاص بدیم.

Form.js :

import React, { Component } from 'react'

class Form extends Component {
  constructor(props) {
    super(props)

    this.initialState = {
      name: '',
      job: '',
    }

    this.state = this.initialState
  }

هدف ما برای این فرم اینه که state در Form رو در هر زمانی که فیلد فرم تغییر کرد آپدیت کنیم، و زمانی که ارسال شد، همه داده ها به State در App پاس داده بشن، که سپس Table آپدیت خواهد شد.

اول، ما یک تابع میسازیم که هر زمانی که ورودی (input) تغییر میکنه اجرا بشه. این event پاس خواهد داد، و ما state در Form رو با داشتن name (key) و Value ورودی ها تنظیم میکنیم.

handleChange = event => {
  const { name, value } = event.target

  this.setState({
    [name]: value,
  })
}

قبل از اینکه ما ارسال فرم رو انتقال بدیم بیاین اینکارو انجام بدیم.در رندر، بیاین دو ویژگی رو از state فرم بگیریم، و به اونها مقدار های که کلید هر فرم مطابقت داره رو اختصاص بدیم. ما تابع handleChange() رو در onChange ورودی (input) اجرا میکنیم، و در آخر ما کامپوننت Form رو خروجی میگیریم.

render() {
  const { name, job } = this.state;

  return (
    <form>
      <label>Name</label>
      <input
        type="text"
        name="name"
        value={name}
        onChange={this.handleChange} />
      <label>Job</label>
      <input
        type="text"
        name="job"
        value={job}
        onChange={this.handleChange} />
    </form>
  );
}

export default Form;

داخل App.js ما میتونیم از جدول زیر رندر بگیریم.

return (
  <div className="container">
    <Table characterData={characters} removeCharacter={this.removeCharacter} />
    <Form />
  </div>
)

حالا اگه به داخل برنامه مون در مرورگر بریم، ما یک فرم میبینم که هنوز قابلیت ارسال نداره. بعضی از فیلد ها رو آپدیت کنید و شما میبینید که state محلی در Form آپدیت میشه.

کوول 🙂 آخرین مرحله اینه که ما به طور واقعی بتونیم داده ها رو به state والد، ارسال و آپدیت کنیم. ما یک تابع بنام handleSubmit() در App درست میکنیم که state مارو با گرفتن this.state.characters موجود آپدیت کنه و یک پارامتر character جدید اضافه میکنیم، از spread operator در ES6 استفاده میکنیم.

handleSubmit = character => {
  this.setState({ characters: [...this.state.characters, character] })
}

حالا ما پاس میدیم از طریق پارامتر به Form

<Form handleSubmit={this.handleSubmit} />

حالا در Form یک تابع بنام SubmitForm() درست میکنیم که تابع رو صدا بزنه و state در فرم رو از طریق پارامتر character که ما قبلا تعریفش کردیم، پاس بده. این همچنین state را به state اولیه بازنشانی خواهد کرد، بعد از ارسال فرم رو پاک کنید.

submitForm = () => {
  this.props.handleSubmit(this.state)
  this.setState(this.initialState)
}

در آخر ما دکمه ارسال را برای ارسال فرم اضافه میکنیم. ما از onClick بجای onSubmit استفاده میکنیم چون ما از عملکرد استاندارد ارسال استفاده نمیکنیم. و با کلیک کردن submitForm که درستش کردیم صدا زده میشه.

<input type="button" value="Submit" onClick={this.submitForm} />

و حالا برناممون کامل شد :). ما میتونیم اضافه و حذف کاربران در جدولمون رو بسازیم. چون Table و TableBody از state گرفته میشن.

اگر شما در هر قسمتی از آموزش دچار مشکل شدید میتونید سورس کامل این پروژه در گیت هاب رو ببینید.

گرفتن داده در Api

یکی از کاربرد های رایج ریکت، گرفتن داده ها از Api هست. اگر شما با api آشنایی نداری یا نمیدونی چطوری باید بهش وصل بشی، من پیشنهاد میکنم How to Connect To an Api with Javascript رو بخونی، که شما از چه طریق به api وصل بشی و استفاده کنی از اون در وانیلا جاوا اسکریپت.

برای یک تست کوچک، ما میتونیم یک فایل Api.js بسازیم، و یک App جدید در اون بسازیم. یک Api عمومی که میتونیم تستش کنیم api ویکی پدیا هست. و من در اینجا یک Url Endpoint برای جستجوی تصادفی دارم. شما میتونید به لینک داده شده برید و api اش رو ببینید – و شما مطمئن باشید که JSONView رو بر روی مرورگرتون نصب کرده اید.

ما قصد داریم از JavaScript’s built-in Fetch برای جمع آوری داده از url endpoint استفاده کنیم و اون رو نمایش بدیم. شما میتونید بین برنامه ای که ساختیم و این فایل تست عوض کنید به وسیله تغییر آدرس در index.js و import App from ‘./Api’ .

من قصد ندارم کد زیر رو خط به خط توضیح بدم، چون قبلا درباره ساخت کامپوننت، رندر شدن، و mapping از طریق یک آرایه state یاد گرفتیم. مورد جدید این قطعه کد componentDidmout() هست، که یک روش lifecycle ریکت هست. lifecycle هست که در داخل اون توابع ریکت صدا زده میشن. Mounting به یک آیتم وارد شده در DOM اشاره میکند.

هنگامی که ما داده های رو از API دریافت میکنیم، ما از componentDidMount استفاده میکنیم، چون ما میخایم مطمئن بشیم که کامپوننت در DOM رندر شده قبل از اینکه ما داده ها رو بیاریم. در قطعه کد زیر، شما میبیند که ما چطور داده ها رو از api ویکی پدیا میگیریم و اونها رو در صفحه نمایش میدیم.

Api.js :

import React, { Component } from 'react'

class App extends Component {
  state = {
    data: [],
  }

  // Code is invoked after the component is mounted/inserted into the DOM tree.
  componentDidMount() {
    const url =
      'https://en.wikipedia.org/w/api.php?action=opensearch&search=Seona+Dancing&format=json&origin=*'

    fetch(url)
      .then(result => result.json())
      .then(result => {
        this.setState({
          data: result,
        })
      })
  }

  render() {
    const { data } = this.state

    const result = data.map((entry, index) => {
      return <li key={index}>{entry}</li>
    })

    return <ul>{result}</ul>
  }
}

export default App

وقتی که شما فایل رو ذخیره و اجرا کنید در سرور محلی، شما میبینید که دادهای api ویکی پدیا در DOM نمایش داده شده اند.

روش های lifecycle دیگه ای هم وجود داره، اما پرداختن به اونها فراتر از محدوده این مقاله هست. شما میتونید درباره کامپوننت های ریکت در اینجا بیشتر بخونید.

ساختن و انتشار یک برنامه React

همه چیز هایی که تابحال انجام دادیم در یک محیط توسعه (development) بود. ما کامپایل میکردیم، از hot-reloading ، و آپدیت بر روی Fly استفاده میکردیم. ولی برای تولید (production)، ما قصد داریم همه فایل های استاتیک رو در هر کد منبعی لود کنیم. ما میتونیم اینکارو با build و deploying انجام بدیم.

حالا اگر شما میخاین همه کد های ریکت رو کامپایل کنید و در پوشه root در هرجایی قرار بدین، شما نیاز دارین که خط زیر ر اجرا کنید:

npm run build

این دستور یک پوشه build میسازه که شامل برنامه ما هست. شما میتونید محتوای پوشه رو هرجایی که خواستین قرار بدید، و شما انجامش دادید 🙂

ما همچنین میتونیم یک قدم جلوتر برداریم، و npm برای ما امکان deploy گذاشته. ما قصد داریم پروژه رو در یک صفحه گیت هاب بیلد (build) کنیم. بنابراین لازمه که شما به گیت آشنا باشید و کد رو در گیت هاب بزارید.

مطمئن شین که از محیط محلی توسعه ریکت خارج شدین، بنابراین الان نباید کد در حال اجرا باشه. ابتدا ما قصد داریم فیلد homepage رو به package.json اضافه کنیم، که آدرسی هست که ما میخام برنامه مون رو بصورت زنده بالا بیاریم.

"homepage": "https://taniarascia.github.io/react-tutorial",

ما همچنین دو خط ویژگی اسکریپت رو اضافه میکنیم.

"scripts": {
  // ...
  "predeploy": "npm run build",
  "deploy": "gh-pages -d build"
}

در پروژتون، شما gh-pages رو در devDependencies اضافه کنید.

npm install --save-dev gh-pages

خب حالا ما پروژه رو بیلد میکنیم، که همه فایل های استاتیک رو کامپل کرده باشه.

npm run build

و در آخری ما منتشرش میکنیم در gh-pages (صفحه گیت هاب)

npm run deploy

و ما انجامش دادیم!هورا 🙂 این برنامه به صورت زنده در آدرس زیر در دسترس هست:

https://taniarascia.github.io/react-tutorial

نتیجه

این مطلب مقدمه خوبی از ریکت به شما داد

simple and class compenent, props, کار با داده فرم، گرفتن داده ها از طریق Api، انتشار (deploying) برنامه. موارد خیلی زیادی برای یادگیری و انجام با ریکت وجود داره، اما امیدوارم احساسی خوشحالی در این آموزش ساده ریکت برای خودتان داشته باشید ^_^

مشاهده سورس کد در گیت هاب

مشاهده پروژه

لطفا اگر هرچیزی شفاف نبود بهم بگین، یا اگر مورد جدیدی رو در این مطلب یا مطالب بعدی دوست دارید باشه بهم بگید.

این آموزش ترجمه شده از آموزش مطلب زیر است که نویسنده اون خانم Tania هست :

لینک منبع اصلی آموزش

من : دوستان اگر مشکلی در ترجمه هست و بخش هایی واضح و روان ترجمه نشده لطفا در نظرات اطلاع بدید.

دیدگاه خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *