در قسمت 6، تمرینی را جهت پیاده سازی نمایش لیست یک سری فیلم، انجام دادیم. در اینجا قصد داریم این تمرین را جهت دریافت امتیاز و Like از کاربر، به ازای هر ردیف نمایش داده شده، تکمیل کنیم.
بررسی ساختار کامپوننت Like
در پوشهی components، ابتدا پوشهی جدید common را ایجاد میکنید. در اینجا تمام کامپوننتهای عمومی برنامه را که منحصر به دومین آن برنامه نیستند، قرار میدهیم. کامپوننتهایی را که اگر آنها را به برنامههای دیگری نیز کپی کردیم، بدون هیچ مشکلی قابلیت استفادهی مجدد را داشته باشند و متصل به سرویسها و زیرساخت برنامهی جاری نباشند. سپس در پوشهی common، فایل جدید src\components\common\like.jsx را ایجاد میکنیم و داخل آن توسط میانبرهای imrc و cc در VSCode، ساختار ابتدایی کامپوننت Like را ایجاد میکنیم.
ساختار کلی این کامپوننت به صورت زیر است:
- ورودی این کامپوننت به این صورت است که در آن مشخص شده آیا یک فیلم، مورد علاقه واقع شده یا خیر؛ مانند خاصیت liked که یک boolean است. اگر true باشد، یک آیکن قلب توپر را نمایش میدهد و برعکس.
- خروجی این کامپوننت نیز به صورت یک رخداد است. هر زمانیکه بر روی آیکن قلب آن کلیک میشود، این کامپوننت یک رخداد onClick را سبب خواهد شد. اکنون هر کامپوننت دیگری که در حال استفادهی از آن است، مطلع شده و خاصیت liked شیء مرتبط را تغییر میدهد.
بنابراین این کامپوننت اطلاعی از ساختار یک شیء movie ندارد. تنها یک DOM کامپوننت ساده است که کارش نمایش یک آیکن قلب توپر یا خالی میباشد و اگر بر روی این آیکن قلب کلیک شد، به والد خود اطلاع رسانی میکند.
فعلا ساختار ابتدایی آنرا به رندر یک قلب خالی که توسط قلم آیکنهای font-awesome تامین میشود، تنظیم میکنیم:
import React, { Component } from "react";
class Like extends Component {
render() {
return <i className="fa fa-heart-o" aria-hidden="true"></i>;
}
}
export default Like;
نمایش ابتدایی کامپوننت Like در جدول لیست فیلمها
فعلا مهم نیست که این کامپوننت کار خاصی را انجام نمیدهد. فقط قصد داریم آنرا در UI برنامه نمایش دهیم. به همین جهت ابتدا یک ستون جدید را مخصوص آن، در جدول فعلی نمایش لیست فیلمها، ایجاد کرده و المان آنرا درج میکنیم. برای این منظور به فایل movies.jsx مراجعه کرده و ابتدا این کامپوننت را import میکنیم:
import Like from "./common/like";
سپس در سرستونهای جدول، یک th جدید را تعریف میکنیم تا ستونی برای درج آن ایجاد شود. همچنین در قسمت بدنهی جدول، پیش از دکمهی حذف، یک td مخصوص درج المان </Like> را اضافه میکنیم:
تا اینجا ستون جدید Like را مشاهده میکنید که کار رندر کامپوننتهای Like در آن انجام شدهاست.
واکنش نشان دادن به ورودیها، در کامپوننت Like
در ادامه باید این کامپوننت بر اساس مقدار Boolean ای که از والد خود دریافت میکند، یک آیکن قلب توپر و یا خالی را نمایش دهد. برای این منظور فعلا در کامپوننت movies، جائیکه المان کامپوننت Like درج شدهاست، ویژگی جدید liked را به مقدار ثابت true تنظیم میکنیم </Like liked={true}> تا بتوان قسمت props این کامپوننت را تکمیل کرد.
در کامپوننت Like، تفاوت بین آیکن قلب توپر و خالی در یک o- در انتهای کلاسهای font-awesome است:
import React, { Component } from "react";
class Like extends Component {
render() {
let classes = "fa fa-heart";
if (!this.props.liked) {
classes += "-o";
}
return <i className={classes} aria-hidden="true"></i>;
}
}
export default Like;
در اینجا اگر بر اساس مقدار ورودی this.props.liked، یک مقدار false را دریافت کردیم، به classes یک o- را اضافه میکنیم تا یک آیکن قلب خالی را رندر کند. سپس این classes را به خاصیت className انتساب دادهایم.
پس از این تغییرات اگر برنامه را ذخیره کرده و مجددا در مرورگر بارگذاری کنیم، با توجه به تنظیم liked={true} در کامپوننت movies، ستون like آن با آیکنهای قلب توپر نمایش داده میشود که بیانگر واکنش نشان دادن صحیح به ورودیها در کامپوننت Like است:
پویا سازی مقدار پیشفرض ویژگی liked در کامپوننت movies
برای پویاسازی نمایش مقدار liked در کامپوننت movies، از آنجائیکه هر ردیف بیانگر یک شیء movie است، میتوان به این صورت عمل کرد:
<Like liked={movie.liked} />
البته اگر به فایل fakeMovieService.js مراجعه کنید، خاصیت liked در ساختار اشیاء فیلمها وجود ندارد که فعلا آنرا برای اولین شیء تعریف شده، اضافه میکنیم:
const movies = [
{
_id: "5b21ca3eeb7f6fbccd471815",
title: "Terminator",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 6,
dailyRentalRate: 2.5,
publishDate: "2018-01-03T19:04:28.809Z",
liked: true
},
پس از این تغییرات، اکنون خروجی برنامه در مرورگر به صورت زیر تغییر کرده که اولین آیتم آن بر اساس مقدار تنظیم شدهی فوق، با آیکن قلب توپر نمایش داده شدهاست:
افزودن رخداد کلیک به کامپوننت Like
برای اینکه کامپوننت Like، رویداد کلیک بر روی آیکن قلب را به والد خود گزارش دهد، ابتدا ویژگی جدید onClick را بر روی تعریف المان آن در کامپوننت movies اضافه میکنیم:
<Like liked={movie.liked} onClick={() => this.handleLike(movie)} />
که به متد handleLike در همان کامپوننت متصل خواهد شد:
handleLike = movie => {
console.log("handleLike", movie);
};
سپس در کامپوننت Like، این ویژگی onClick را از طریق خاصیت props دریافت کرده و به رویداد onClick المان نمایش آیکن، متصل میکنیم:
return (
<i
className={classes}
onClick={this.props.onClick}
aria-hidden="true"
style={{ cursor: "pointer" }}
></i>
);
اینبار اگر بر روی المان نمایش آیکن کلیک شود، سبب فراخوانی متد handleLike والد متصل به آن خواهد شد.
در اینجا همچنین style این المان نیز جهت نمایش cursor با آیکن pointer، توسط یک شیء از نوع inline style تنظیم شدهاست.
یک نکته: کامپوننت Like تا اینجا یک controlled component است؛ دارای state نیست و همچنین تمام اطلاعات خودش را از طریق props تامین میکند و تنها دارای یک متد render است. بنابراین اگر علاقمند بودید میتوان این کامپوننت را به یک «Stateless Functional Component» که در
قسمت 8 معرفی شد نیز تبدیل کرد.
تغییر حالت کامپوننت Like جهت نمایش تغییرات
تا اینجا کامپوننت Like ما میتواند ورودی true/false را به آیکنهای متناظری تبدیل کند. همچنین اگر بر روی این آیکن کلیک شود، آنرا توسط رخدادی به والد خود اطلاع رسانی میکند. اکنون میخواهیم با تکمیل متد handleLike، خاصیت like اشیاء انتخابی (آیکنهایی که بر روی آنها کلیک شدهاند) را از true به false و برعکس تبدیل کرده و سپس UI را نیز به روز رسانی کنیم:
handleLike = movie => {
console.log("handleLike", movie);
const movies = [...this.state.movies]; // cloning an array
const index = movies.indexOf(movie);
movies[index] = { ...movies[index] }; // cloning an object
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
با یک چنین مثالی که در آن cloning اشیاء و آرایهها صورت میگیرد،
پیشتر آشنا شدهاید. هدف از cloning، قطع ارتباط شیء، یا آرایهی ایجاد شده، از شیء، یا آرایهی اصلی است تا با اعمال تغییرات بر روی شیء clone شده، تغییری در شیء اصلی صورت نگیرد؛ چون در React مجاز به تغییر مستقیم اشیاء state نیستیم.
پس از این تغییرات اگر برنامه را اجرا کنیم، با کلیک بر روی هر آیکن، عکس آن آیکن نمایش داده میشود؛ برای مثال آیکن قلب توپر، تبدیل به آیکن قلب توخالی خواهد شد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
sample-10.zip