هر چیزی که نیاز دارید در مورد Functional و class Component در React بدونید


در مقاله ReactJs چیست و چرا باید ازش استفاده کنیم،یک سری ویژگی ها مثل سادگی، تست پذیری، عملکرد و … لیست کردیم برای دلایلی که React رو باید یاد گرفت.تیم React هر روز یک سری ویژگی ها رو اضافه می کنند که یادگیری و کدنویسی رو راحت تر می کنه.در این مقاله قصد داریم یک راه رو معرفی کنیم که یادگیری،تست و عملکرد رو بهتر میکنه.اگر به علاقه مند هستید که همگی این اتفاقات خوب بیفته در ادامه با من همراه باشید تا بدونید که functional components راهی برای رسیدن به این ویژگی ها است.همچنین شما هر چیزی که نیاز دارید در مورد اینکه چرا، چگونه و چه زمانی از functional components در React استفاده کنید،یاد خواهید گرفت.شما فقط نیاز به درک اولیه React و دانستن ES6 دارید.
Functional Components چیست؟
دو نوع کامپوننت ها در React وجود داره Class Components و Functional Components.تفاوت بین این دو type خیلی واضح هست.Class components در واقع کلاس های ES6 هستند و Functional Components توابع هستند.
1 2 3 |
function Hello(props){ return <div>Hello {props.name}</div> } |
قطعه کد بالا یک نمونه ساده از functional component ها است.شاید تنها چیزی که ذهن شما رو درگیر کرده باشه اینکه در functional component ها میتونیم از state و lifecycle methodes استفاده کنیم یا نه. تا قبل از معرفی hooks استفاده از state ها و lifecycle ها امکان پذیر نبود.
قطعه کد زیر مشابه کامپوننت بالا است اما با ES6 نوشته شده:
1 |
const Hello = ({name}) => <div>Hello {name}</div> |
اگر شما قبلا ES6 رو ندید،وحشت نکنید.ممکنه که یکم عجیب و غریب به نظر برسه ولی اصلا پیچیده نیست.
قطعه کد زیر مشابه کامپوننت بالا است اما به عنوان class component نوشته شده :
1 2 3 4 5 |
class Hello extends Component{ render(){ return <div>Hello {this.props.name}</div> } } |
اگر کامپوننت شما یک کلاس دارید که فقط render method داره،بدون شک باید از functional component برای ایجاد کامپوننت استفاده کنید.هر چند که با وجود hooks شخصا معتقد هستم که همیشه از functional component برای ایجاد کامپوننت ها در React استفاده کنید.
تا حالا تفاوت های اصلی بین functional component و class component رو مشاهده کردید.این یک مثال ساده است.بهتره که خودتون یک سری تغییرات ایجاد کنید و تفاوت ها رو مشاهده کنید.
Functional Components کدها را برای فهمیدن ساده تر می کند
یکی از مزایای functional components اینکه کدها رو برای خواندن و فهمیدن ساده تر می کنه.اگر شما بر روی یک پروژه شخصی کار میکنید، شاید زیاد به این موضوع توجه نکنید.اگر شما بعد از یک ماه به پروژه ای که نوشتید برگردید،به احتمال زیاد درک کردن کدهایی که خودتون نوشتید،سخت خواهد بود.پیشنهاد میکنم نقل قول obert Martin رو در این کتاب در مورد Clean Code بخونید.
” نسبت زمان صرف شده برای خواندن و نوشتن 10 به 1 است.نوشتن آسان است ولی خواندن سخت”
یعنی شما شاید خیلی راحت کد می نویسید ولی زمانی که بعد مدت ها بر میگردید و کدها رو مرور می کنید باید 10 برار زمان نوشتن وقت بزارید تا کدها رو درک کنید.
این مزیت تا زمانی که شما در دو حالت کامپوننت ها رو بنویسید شاید براتون قابل درک نباشه.پس پیشنهاد میکنم حتما تست کنید و از نتایج نوشتن کامپوننت ها با function لذت ببرید.
Functional Components عملکرد بهتری دارند
به جمله زیر در release notes توجه کنید:
“در آینده ما قادر خواهیم بود که بهینه سازی عملکرد بهتری برای کامپوننت ها از طریق خودداری از چک های بیهوده و تخصیص حافظه داشته باشیم”
حتی بدون این بهینه سازی نتیجه تبدیل کد ها توسط babel خیلی کمتر از class component ها است.نتیجه خروجی کامپوننت هایی که در بالا با class و functionنوشتیم در سایت babel به صورت زیره:
DEV – function component:
1 2 3 4 5 |
import React from 'react'; const HelloWorld = () => <div>Hello World</div>; export default HelloWorld; |
PROD – functional component l
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireDefault(require("react")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var HelloWorld = function HelloWorld() { return _react.default.createElement("div", null, "Hello World"); }; var _default = HelloWorld; exports.default = _default; |
DEV – class component:
1 2 3 4 5 6 7 8 9 10 11 |
import React, { PureComponent } from 'react'; class HelloWorld extends React.PureComponent { render() { return ( <div>Hello World</div> ); } } export default HelloWorld; |
PROD – class component
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 42 43 44 45 46 47 48 49 50 51 52 53 54 |
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } } function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } var HelloWorld = /*#__PURE__*/ function (_React$PureComponent) { _inherits(HelloWorld, _React$PureComponent); function HelloWorld() { _classCallCheck(this, HelloWorld); return _possibleConstructorReturn(this, _getPrototypeOf(HelloWorld).apply(this, arguments)); } _createClass(HelloWorld, [{ key: "render", value: function render() { return _react.default.createElement("div", null, "Hello World"); } }]); return HelloWorld; }(_react.default.PureComponent); var _default = HelloWorld; exports.default = _default; |
به نسخه های build بالا اگر توجه کنید می بینید که یک component ساده در نسخه build به چه هیولایی تبدیل شده.ممکنه باور این موضوع براتون سخت باشه ولی خودتون می تونید هر کامپوننتی رو در هر دو حالت تست کنید و خروجی ها رو ببینید.
Functional Components بیشتر reusable هستند
این مورد ممکن است یکم بحث برانگیز باشد.اگر ما در function components از state ها استفاده نکنیم،کامپوننت ها راحت تر پیاده سازی می شوند و خیلی راحت در قسمت های دیگه پروژه و پروژه های دیگه قابل استفاده هستند.در ادامه کامپوننت checkbox رو با هر دو نوع پیاده سازی می کنیم.
Functional Checkbox:
1 2 3 4 5 6 7 8 9 10 11 |
const Checkbox = ({ checked, label, handleClick }) => ( <div className={checked ? 'Checkbox-container checked' : 'Checkbox-container'} onClick={handleClick} role="button" tabIndex={0} data-label={label} > <p className="label" data-label={label}>{label</p> </div> ); |
کامپوننت Checkbox در واقع حالت پیشفرضی ندارد و عموما چندین چک باکس کنار یکدیگر قرار می گیرد و کاربر هر کدام که مورد نیازش است را انتخاب می کند.به طور مثال سناریویی را در نظر بگیرید که کاربر قرار است وسایل نقلیه ای که دارد را مشخص کند بنابراین چندین چک باکس وجود دارد که کنار هر کدام می تواند عبارتی مثل خودرو،دوچرخه و موتور سیکلت باشد.پس در واقع کامپوننت چک باکس چندین بار مورد استفاده قرار میگیرد با این تفاوت که محتوای آن تفاوت دارد.شاید این سوال در ذهن شما به وجود آمده باشد که با class ها هم میتوان کامپوننت Checkbox رو نوشت؟100 درصد میشه اما functional component ما را مجبور می کند که best practices ها رو رعایت کنیم تا نتیجه بهتری داشته باشیم و کامپوننت بیشتر reusable باشد.اگر به کامپوننت بالا توجه کردع باشید مدیریت کلیک کردن در کامپوننتی که Checkbox در آن wrap شده است اتفاق می افتد.همین عملیات باعث افزایش reusable بودن کامپوننت می شود.
PropTypes
بنا به بعضی دلایل بعضی از مردم فکر می کنند که نمی شود PropTypes را با functional components استفاده کرد.اما در واقع یک تفکر اشتباه است.در تیکه کد پایین من PropTypes رو بهCheckbox component اضافه کردم.
1 2 3 4 5 6 7 8 9 |
Checkbox.propTypes = { checked: PropTypes.bool, label: PropTypes.string.isRequired, handleClick: PropTypes.func.isRequired, }; Checkbox.defaultProps = { checked: true, }; |
Class Checkbox:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
render(){ return( <div className={checked ? 'Checkbox-container checked' : 'Checkbox-container'} onClick={handleClick} role="button" tabIndex={0} data-label={label} > <p className="label" data-label={label}>{label</p> </div> ); } |
در نگاه اول به render method در class-based component در کد بالا بسیار شبیه function component است به جزء کلمه کلیدی “this” که برای مدیریت حالت چک باکس به چشم می خورد.یک کم گیج کننده به نظر می رسد اما زیاد بد نیست.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Checkbox extends Component{ constructor(){ super(); this.state = { checked: false, }; this.handleClick = this.handleClick.bind(this); } handleClick(){ this.setState({ checked: !this.state.checked, }) } |
حالا به قسمت دیگر کامپوننت Checkbox نگاه کنید.تمامی این قسمت از کد ها در functional component نیست.11 خط کد در مقایسه با 30 خط کد class component.کدهای functional components به وضوح بسیار راحت تر برای فهم هستند
با وجود کدهای ترسناک این کامپوننت همان کار را انجام می دهد،اما تمامی وضعیت ها رو خودش مدیریت می کند.بنابراین ما خودمون مشخص می کنیم که چه حالت های پیشفرضی وجود داشته باشد.اگر ما بخواهیم که یعضی از چک باکس ها به صورت پیشفرض true باشید باید چکار کنیم؟یا اگر ما قصد داشته باشیم که چک باکس حالت toggle را نداشته باشد؟
پس بهتر است که از functional component استفاده کنیم تا بتوانیم تمامی حالت ها رو خودمون مدیریت کنیم و این دقیقا یعنی more reusable.
توجه داشته باشید که تا قبل از نسخه 16.8 و معرفی hooks شما نمی توانستید از state و lifecycle methodes استفاده کنید.ولی با معرفی hooks این مشکل نیز بر طرف شد و شما می توانید تمامی کامپوننت های مورد نیازتون رو با function بنویسید.
اگر قصد یادگیری react رو به صورت تخصصی و حرفه ای دارید، پیشنهاد می کنم آموزش جامع و پروژه محور react (ری اکت ) رو مشاهده کنید
مطالب زیر را حتما مطالعه کنید
ویژگی ها و قابلیت های جدید react 18
فریمورک های Frontend که باید در سال 2021 یاد بگیرید
نگاه اولیه به React Server Component
React Higher Order Components چیست
5 React Best Practice که باید در سال 2020 یاد بگیرید
Hook های مفیدی که باید در پروژه بعدی React خود از آن استفاده کنید
6 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
سلام
بابت مطالب خوبتون که تو سایت گذاشتین ممنون
خدا اجرتون بده.
خیلی ممنون مرتضی جان
مقاله جالبی بود .! اینکه رفرنس های که ازش استفاده کردین گذاشتین خیلی عالی بود . چون به اونم میتونیم دسترسی داشته باشیم ♥
خوشحالیم که براتون مفید بوده
مقاله ی عالی بود مرسی خیلی کمک کرد بهم
از این بابت خیلی خوشحالیم امید عزیز
اگه این پست برات مفید بوده لطفا با دوستانت هم به اشتراک بزار
موفق باشی