کار با داده ها (Data Driven) و Props در Reactjs

17 اسفند 1397
درسنامه درس 5 از سری آموزش react (ری اکت)
React-props

 قرار دادن داده ها بطور مستقیم در داخل برنامه کار خوبی نیست. در این آموزش ما قابلیت کار با داده ها را به برنامه مان اضافه می کنیم تا برنامه به راحتی بتواند به داده های خارجی دسترسی داشته باشد.

تا به اینجای آموزش ما اولین کامپوننت مان را نوشتیم و رابطه فرزند/والد را هم برای آن تنظیم کردیم. اما این کامپوننت هنوز از داده ها استفاده نمی کند. بنابراین این برنامه نمی تواند از قدرت ری اکت برای نمایش داده های داینامیک یا پویا استفاده کند. در این آموزش قصد افزودن قابلیت کار با داده ها در ری اکت را داریم.

ساخت یک برنامه داده محور در Reactjs (ری اکت)

اگر به یاد داشته باشید در آموزش قبلی دیدید که ما یک کامپوننت timeline که شامل یک هدر و یک لیستی از فعالیت (اکتیویتی)های مختلف بود، را نوشته ایم

ساخت کامپوننت timeline در ری اکت
ساخت کامپوننت timeline در ری اکت

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

در این آموزش می خواهیم از این کامپوننت ها برای نمایش داده استفاده کنیم. بنابراین از کامپوننت <Header/> استفاده می کنیم.

همان طور که مشخص است کامپوننت <Header/> فقط عنوان کامپوننت timeline را نمایش می دهد. این یک عنصر خیلی مفیدی است، چون می توانیم از آن در قسمت های مختلف برنامه مان استفاده کنیم، اما مشکلی که وجود دارد این است که هر کامپوننت timeline باید عنوان های مختلفی داشته باشد.

حال باید به ری اکت بگوییم که هر کدام از این کامپوننت های timeline باید عنوان های مختص به خود را داشته باشند.

مقدمه ای بر props در ری اکت

ری اکت به ما اجازه می دهد تا به همان طریقی که به یک عنصر html داده ای را ارسال می کنیم، به کامپوننت ری اکت هم توسط خصیصه یا پروپرتی داده ای را ارسال کنیم.

اینکار مشابه خصیصه src برای یک تگ تصویر است. ما می توانیم به پروپرتی های تگی مانند <img> همانند یک prop نگاه کنیم که به کامپوننت های ری اکت اعمال می شود.

همچنین برای دسترسی به مقادیر این پروپرتی ها در داخل کامپوننت از this.props استفاده می کنیم. در آموزش های قبلی، کامپوننت <Header/> را مانند زیر تعریف کردیم.

lass Header extends React.Component {
  render() {
    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className="searchInput"
          placeholder="Search ..." />

        <div className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

هنگام استفاده از کامپوننت <Header/> آن را داخل کامپوننت <App/> مانند زیر قرار می دهیم.

<Header />

کامپوننت هدر

حال می توانیم عنوان هر هدر را توسط پروپرتی title  به کامپوننت <Header/> ارسال کنیم.

<Header title="Timeline" />

مشخص کردن عنوان برای کامپوننت timeline

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

import React from 'react'

class Header extends React.Component {
  render() {
    return (
      <div className="header">
        <div className="menuIcon">
          <div className="dashTop"></div>
          <div className="dashBottom"></div>
          <div className="circle"></div>
        </div>

        <span className="title">
          {this.props.title}
        </span>

        <input
          type="text"
          className="searchInput"
          placeholder="Search ..." />

        <div className="fa fa-search searchIcon"></div>
      </div>
    )
  }
}

export default Header

در بالا کدهای کامپوننت <Header/> را کمی تغییر دادیم تا کارهای خواسته شده را بهتر انجام دهد. همچنین یک کلاس searchIcon و menuIcon هم برای استایل دهی به این کامپوننت اضافه کردیم.

حال هنگام فراخوانی کامپوننت <Header/> می توانیم توسط پروپرتی title، یک عنوان را به کامپوننت ارسال کنیم تا در برنامه نمایش داده شوند. برای مثال می توانیم کامپوننت <Header/> را مانند زیر فراخوانی کنیم.

<Header title="Timeline" />
<Header title="Profile" />
<Header title="Settings" />
<Header title="Chat" />

نتیجه اجرا شامل چهار کامپوننت مانند زیر است که هر کدام عنوان های متفاوتی دارد.

چهار کامپوننت مربوط به Header در عناوین مختلف
چهار کامپوننت مربوط به Header در عناوین مختلف

حال می توانیم از کامپوننت <Header/> که یک پروپرتی داینامیک یا پویا به نام title دارد در قسمت های مختلف برنامه استفاده کنیم.

همچنین می توانیم به جز نوع داده رشته ای، انواع دیگری هم به کامپوننت ارسال کنیم، مانند اعداد، رشته، آبجکت ها و حتی توابع.

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

همانند عناصر html، کامپوننت های ری اکت هم می توانند چندین پروپرتی داشته باشند. در آموزش قبلی ،کامپوننت content را به شکل زیر تعریف کردیم:

class Content extends React.Component {
  render() {
    return (
      <div className="content">
        <div className="line"></div>

      {/* Timeline item */}
        <div className="item">
          <div className="avatar">
            <img src="http://www.croop.cl/UI/twitter/images/doug.jpg" />
            Doug
          </div>

          <span className="time">
            An hour ago
          </span>
          <p>Ate lunch</p>
          <div className="commentCount">
            2
          </div>
        </div>

        {/* ... */}

      </div>
    )
  }
}

پروپرتی های کامپوننت محتوا نیاز به موارد زیر دارند:

  • یک تصویر آواتار کاربران
  • یک timestamp برای اکتیویتی (فعالیت)ها
  • متن آیتم های اکتیویتی
  • تعداد کامنت

حال فرض کنید که یک آبجکت جاوا اسکریپت دارید که یک آیتم اکتیویتی را نمایش می دهد. ما چند فیلد از قبیل فیلد متنی و یک آبجکت date داریم. همچنین چند آبجکت دیگر از قبیل user و comment هم داریم.

برای مثال:

{
  timestamp: new Date().getTime(),
  text: "Ate lunch",
  user: {
    id: 1,
    name: 'Nate',
    avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
  },
  comments: [
    { from: 'Ari', text: 'Me too!' }
  ]
}

مشابه یک مقدار رشته ای که به کامپوننت <Header/> پاس دادیم، می توانیم این آبجکت اکتیویتی را گرفته و آن را به کامپوننت content پاس دهیم. حال کامپوننت مان را برای نمایش جزئیات این اکتیویتی در داخل قالب تغییر می دهیم.

به علاوه برای ارسال مقدار یک متغیر داینامیک یا پویا به داخل قالب باید از سینتکس قالب (template syntax) برای رندر آن در داخل قالب برنامه استفاده کنیم. برای نمونه:

import React from 'react'

class Content extends React.Component {
  render() {
    const {activity} = this.props; // ES6 destructuring
    
    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        <div className="item">
          <div className="avatar">
            <img
              alt={activity.text}
              src={activity.user.avatar} />
            {activity.user.name}
          </div>

          <span className="time">
            {activity.timestamp}
          </span>
          <p>{activity.text}</p>
          <div className="commentCount">
            {activity.comments.length}
          </div>
        </div>
      </div>
    )
  }
}

export default Content

در اولین خط تابع render از یک کد ES6 به نام destructuring استفاده کردیم.

دو خط زیر دقیقا معادل هم هستند.

// these lines do the same thing
const activity = this.props.activity;
const {activity} = this.props;

destructuring به ما اجازه می دهد تا تعریف متغیرها را به روش کوتاه تری انجام دهیم. سپس با ارسال یک آبجکت به عنوان پروپرتی می توانیم از این محتوای جدید استفاده کنیم.

<Content activity={moment1} />

کامپوننت conent

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

با این حال، ممکن است دیده باشید که ما این کامپوننت ها را چندین بار با نظرات مختلف پیاده سازی کرده ایم. در عوض ما می توانیم یک آرایه ای از آبجکت ها را به داخل کامپوننت ارسال کنیم.

فرض کنید یک آبجکت داریم که شامل چندین آیتم اکتیویتی است:

const activities = [
  {
    timestamp: new Date().getTime(),
    text: "Ate lunch",
    user: {
      id: 1, name: 'Nate',
      avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
    },
    comments: [{ from: 'Ari', text: 'Me too!' }]
  },
  {
    timestamp: new Date().getTime(),
    text: "Woke up early for a beautiful run",
    user: {
      id: 2, name: 'Ari',
      avatar: "http://www.croop.cl/UI/twitter/images/doug.jpg"
    },
    comments: [{ from: 'Nate', text: 'I am so jealous' }]
  },
]

همچنین می توانید با ارسال چند اکتیویتی بصورت همزمان به کامپوننت <Content/> محتواهای بیشتری ایجاد کنید.

<Content activities={activities} />

حال اگر صفحه را رفرش کنید، چیزی را مشاهده نخواهید کرد، چون ابتدا باید کامپوننت content را بروزرسانی کنیم تا بتواند چندین اکتیویتی را دریافت کند. همان طور که قبلاً یاد گرفتید، jsx در واقع همان جاوا اسکریپت است که توسط مرورگر اجرا می شود. ما می توانیم توابع جاوا اسکریپت را داخل jsx اجرا کنیم، چون این کدهای jsx همانند سایر کدهای جاوا اسکریپت در مرورگر اجرا می شود. حال باید کدهای jsx اکتیویتی را به داخل تابع map که به ازای هر آیتم یکبار اجرا می شود، منتقل کنیم.

import React from 'react'

class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring
    
    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => {
          return (
            <div className="item">
              <div className="avatar">
                <img
                  alt={activity.text}
                  src={activity.user.avatar} />
                {activity.user.name}
              </div>

              <span className="time">
                {activity.timestamp}
              </span>
              <p>{activity.text}</p>
              <div className="commentCount">
                {activity.comments.length}
              </div>
            </div>
          );
        })}
        
      </div>
    )
  }
}

export default Content

کامپوننت content

حال می توانیم هر تعداد اکتیویتی که خواستیم را به داخل آرایه ارسال کنیم تا کامپوننت content آنها را مدیریت کند.

آیتم های اکتیویتی (فعالیت ها)

دراین قسمت یک کامپوننت برای نمایش یک آیتم اکتیویتی ایجاد می کنیم. همچنین به جای نوشتن یک کامپوننت content پیچیده، می خواهیم یک کامپوننت دیگر برای نمایش آیتم های اکتیویتی بوجود بیاوریم تا به این ترتیب واکنش پذیری برنامه بالا برود.

این کار تست برنامه را راحت تر کرده و در آینده می توانیم به راحتی قابلیت های دیگری را به آن اضافه کنیم.

حال کامپوننت content را برای نمایش یک لیستی از کامپوننت های ActivityItem بروزرسانی می کنیم.

import React from 'react'

import ActivityItem from './ActivityItem';

class Content extends React.Component {
  render() {
    const {activities} = this.props; // ES6 destructuring
    
    return (
      <div className="content">
        <div className="line"></div>

        {/* Timeline item */}
        {activities.map((activity) => (
          <ActivityItem
            activity={activity} />
        ))}
        
      </div>
    )
  }
}

export default Content

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

حال کامپوننت ActivityItem را ایجاد می کنیم. چون ما قبلاً یک view برای ActivityItem ایجاد کرده بودیم، تنها کاری که باید بکنیم این است که آن را از کامپوننت Content کپی کنیم و در یک ماژول مستقل مختص به خودش قرار دهیم.

import React from 'react'

class ActivityItem extends React.Component {
  render() {
    const {activity} = this.props; // ES6 destructuring
    
    return (
      <div className="item">
        <div className="avatar">
          <img 
            alt={activity.text} 
            src={activity.user.avatar} />
          {activity.user.name}
        </div>

        <span className="time">
          {activity.timestamp}
        </span>
        <p>{activity.text}</p>
        <div className="commentCount">
          {activity.comments.length}
        </div>
      </div>
    )
  }
}

export default ActivityItem

آیتم های اکتیویتی

در این هفته، با کمک props کامپوننت هایمان را داده محور کردیم. در بخش بعدی به کامپوننت های stateful می پردازیم.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش react (ری اکت) توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر
25 اردیبهشت 1398
سلام و خسته نباشید. کدها یکم واضح نیستند. بعد هدر که رفتید سراغ کانتنت همه چی بهم گره خورد. مثلا همون اول که یه ابجکت تعریف کردید اصلا هیچ اسمی نداره و فقط داخل پرانتز باز و بسته گذاشتید و اینکه اصلا نگفتید این ابجکت رو کجای برنامه دقیقا باید تعریف کنیم. من همه چی رو امتحان کردم و کلی گشتم ولی متاسفانه مشکلم حل نشد و همش از پراپس ها ارور میگیرم. لطفا راهنمایی و رسیدگی کنید. ممنون از آموزشتون

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر
27 اردیبهشت 1398
ممنونم. مشکل حل شد. مرسی از آموزش خوبتون.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.