جادوی styled-component ها

جادوی styled-component ها
13 مهر 1399, خواندن در 8 دقیقه

اگر تا به حال با styled-component ها روبرو نشده‌اید، قطعه کد زیر را به عنوان یک کامپوننت (مولفه‌ی) استایل ری‌اکت مشاهده کنید:

const Button = styled.button`
  background-color: papayawhip;
  border-radius: 3px;
  color: palevioletred;
`

این متغیر Button تبدیل به یک کامپوننت ری‌اکتی شده که می‌توان از این پس همانند دیگر کامپوننت‌های ری‌اکت، این دکمه را نیز رندر کرد:

<Button>Hi Dad!</Button>

خب حال به نظر شما این فرآیند چگونه عملی شده است؟ به نظر شما از چه ابزاری برای محیا کردن این بستر استفاده شده است؟

Tagged Template Literals

ممکن است بخش styled.button`` کمی برایتان عجیب بوده باشد و با آن آشنایی نداشته باشید. در واقع در این بخش شما در حال استفاده از یکی از قابلیت‌های نسبتا جدید جاوااسکریپت به نام Template Literal ها هستید. این ویژگی جدید جاوااسکریپت با عنوان Tagged Template Literal در ES6 معرفی شده است.

در واقع این بخش کد در حال فراخوانی یک فانکشن است و باید بدانیم که styled.button`` تفاوت چندانی با styled.button() ندارد. تفاوت این دو نوع فراخوانی تنها زمان پاس داده شدن آرگومان‌ها به آن‌ها مشخص خواهد شد.

بیایید برای درک بهتر این نوع فراخوانی، یک مثال ساده بیاوریم:

const logArgs = (...args) => console.log(...args)

ما فانکشنی بسیار ساده تعریف کرده‌ایم که تنها وظیفه‌ی آن لاگ گرفتن از آرگومان‌های پاس داده شده به آن است.

شما می‌توانید قطعه کدهای آورده شده در این مقاله را، همراه با ما، مرحله به مرحله با کپی پیست کردن در کنسول مرورگر کامپیوتر خود و اجرا کردنشان، تست کنید.

در زیر می‌توانید یک مثال ساده از کاربرد این فانکشن تعریف شده را مشاهده کنید:

logArgs('a', 'b')
// -> a b

حال بیاید همان فانکشن را با استفاده از شیوه‌ی جدید فراخوانی (tagged template literal)، فراخوانی کنیم:

logArgs``
// -> [""]

قطعه کد بالا یک آرایه که شامل یک عضو رشته‌ای و خالی می‌شود را لاگ می‌کند. اما جالب این‌ جاست که ما تنها یک رشته را پاس داده‌ایم اما یک آرایه در حال لاگ شدن است!

logArgs`I like pizza`
// -> ["I like pizza"]

با توجه به مثال دیگری که در بالا آورده‌ایم، به نظر می‌رسد که اولین مقدار آرایه‌ی خروجی، همان مقداری است که به فانکشن پاس داده‌ایم. حال بیایید راجع به این که چرا یک آرایه لاگ گرفته می‌شود صحبت کنیم.

Interpolations

Template literal ها یک قابلیت به نام Interpolation (الحاق) را دارا هستند که به آن‌ها امکان استفاده شدن به شکل زیر را می‌دهد:

`I like ${favoriteFood}`

حال بیاید در فراخوانی سنتی (با استفاده از پرانتز) از فانکشن قدیمی‌مان، از این قابلیت نیز استفاده کنیم و بتوانیم در تمپلت لیترال، از متغیر استفاده کنیم:

const favoriteFood = 'pizza'

logArgs(`I like ${favoriteFood}.`)
// -> I like pizza.

با توجه به خروجی کد بالا، متوجه می‌شویم که جاوا اسکرپیت به جای‌گذاری مقدار متغیر favoriteFood پرداخته است و فانکشن را با توجه به آن عملیاتی کرده است.

حال به نظر شما اگر در فراخوانی به روش جدید (با استفاده از ‍‍` `) از Interpolation ها استفاده کنیم، چه اتفاقی می‌افتد؟

const favoriteFood = 'pizza'

logArgs`I like ${favoriteFood}.`
// -> ["I like ", "."] "pizza"

همانطور که در بالا می‌توانید مشاهده کنید، اتفاق عجیبی رخ داده است. ما این بار به جای پاس دادن یک رشته‌،‌ مثل "I like pizza" از الحاق در تمپلت لیترال‌ها نیز استفاده کرده‌ایم.

باز هم همانند فراخوانی قبلی که از ` ` استفاده کردیم، در قسمت اول خروجی، شاهد یک آرایه هستیم؛ اما این بار دارای دو عضو است که اولی قسمت رشته‌ای قبل از الحاق است و دومی نیز رشته ای شامل نقطه‌ی بعد از الحاق و متغیر favoriteFood است. محتوای الحاق شده نیز که همان متغیر ماست، به عنوان دومین آرگومان به فانکشن پاس داده شده‌است.

در نتیجه مشاهده می‌کنیم که با فراخوانی فانکشن‌ها به صورت tagged template literal ها، فانکشن ورودی‌های خود را به شکل جدا از هم دریافت می‌کند؛ بدین ترتیب که ابتدا به عنوان آرگومان اول، در یک آرایه متن‌های رشته‌ای را دریافت می‌کند و سپس به عنوان آرگومان دوم، قسمت‌های الحاقی یا همان interpolation ها را دریافت می‌کند.

در زیر می‌توانید حالتی را مشاهده کنید که به جای یک الحاق، دارای چندین الحاق است:

const favoriteFood = 'pizza'
const favoriteDrink = 'obi'

logArgs`I like ${favoriteFood} and ${favoriteDrink}.`
// -> ["I like ", " and ", "."] "pizza" "obi"

در واقع نکته‌ی قابل ذکر در مثال آخر این است که باید توجه داشه‌ باشیم که هر الحاق به عنوان آرگومان بعدی فانکشن به حساب خواهد آمد و شما می‌توانید بی‌نهایت الحاق را در ورودی فانکشن جای دهید.

حال خروجی قطعه کد بالا را با با خروجی قطعه کدی که شامل فراخوانی سنتی فانشکن است، مقایسه کنید:

const favoriteFood = 'pizza'
const favoriteDrink = 'obi'

logArgs(`I like ${favoriteFood} and ${favoriteDrink}.`)
// -> I like pizza and obi.

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

روش فراخوانی جدید فانکشن‌ها، به چه دردی می‌خورد؟

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

این اتفاق می‌تواند امکانات جدید و جالبی به ما بدهد و همان‌طور که احتمالا حدس زده‌اید، از این قابلیت‌ها در پیاده‌سازی styled-component ها استفاده شده است.

در کامپوننت‌های ری‌اکت، شما انتظار دارید که بتوانید هر کدام از ‌آن‌ها را با استفاده از پراپرتی‌ (prop) هایشان تغییر دهید و شخصی سازی یا کاستومایز کنید. حال تصور کنید کامپوننت <Button /> ای دارید که مثلا باید هنگامی که پراپرتی primary به آن پاس داده می‌شود، بزرگ‌تر از حالت عادی شود و تبدیل به <Button primary /> شود.

وقتی که شما به styled-component ها یک فانکشن الحاقی را پاس می‌دهید، در حال پاس داد پراپرتی‌هایی به آن کامپوننت هستید که می‌توانید از آن‌ها برای ایجاد تغییرات ظاهری و استایل‌دهی در آن کامپوننت استفاده کنید:

const Button = styled.button`
  font-size: ${props => props.primary ? '2em' : '1em'};

حال کامپوننت دکمه‌ی ما، اگر primary باشد، فونت سایز 2em را دریافت خواهد کرد و در غیر این‌صورت، فونت سایز 1em را خواهد داشت.

// font-size: 2em;
<Button primary />

// font-size: 1em;
<Button />

حال بیایید به فانکشن مثال خود برگردیم. بیاید آن را با استفاده از تمپلت لیترال‌ها فراخوانی کنیم و درون آن از فانکشن الحاق نیز استفاده کنیم؛ یعنی همان کاری که در بالا توسط styled.button انجام شد.

logArgs(`Test ${() => console.log('test')}`)
// -> Test () => console.log('test')

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

حال با استفاده از روش جدید همین فراخوانی را انجام می‌دهیم:

logArgs`Test ${() => console.log('test')}`
// -> ["Test", ""] () => console.log('test')

در این حالت، می‌بینیم که فانکشن پاس داده شده در تمپلت لیترال، به عنوان آرگومان دوم به فانکشن ما وارد شده است و نه مثل یک رشته بلکه مانند یک فانکشن! اگر این قطعه کد را در کنسول مرورگرتان اجرا کنید، منظورمان را بهتر متوجه می‌شوید.

پس در واقع فانکشن logArgs می‌تواند به فانکشن پاس داده شده در تمپلت لیترال به آن، به عنوان یک فانکشن عملیاتی دسترسی داشته باشد.

برای بهتر متوجه شدن این بخش نیز بیاید برای مثال یک فانکشن جدید تعریف کنیم که هر فانکشنی را که به عنوان آرگومان دریافت می‌کند را اجرا می‌کند:

const execFuncArgs = (...args) => args.forEach(arg => {
  if (typeof arg === 'function') {
    arg()
  }
})

این فانکشن در زمان اجرای خود،‌ تمام آرگومان های غیر فانکشنی را نادیده گرفته و تنها آرگومان‌هایی که از جنس فانکشن باشند را اجرا خواهد کرد.

execFuncArgs('a', 'b')
// -> undefined

execFuncArgs(() => { console.log('this is a function') })
// -> "this is a function"

execFuncArgs('a', () => { console.log('another one') })
// -> "another one"

حال بیایید این فانکشن را با استفاده از پرانتری که درون آن از تمپلت لیترال استفاده شده است، فراخوانی کنیم:

execFuncArgs(`Hi, ${() => { console.log('Executed!') }}`)
// -> undefined

می‌بینیم که اتفاقی نیفتاده است؛ چرا که این فانکشن در حال دریافت یک آروگومان رشته ایست و فانکشنی را به عنوان آرگومان دریافت نمی‌کند.

حال بیایید با استفاده از جدیدترین روش (tagged template literal) این فانکشن را فراخوانی کنیم:

execFuncArgs`Hi, ${() => { console.log('Executed!') }}`
// -> "Executed!

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

جمع‌بندی

به شما تبریک می‌گوییم؛ چرا که با درک مفهوم بیان شده در این مقاله، به شکل جامع متوجه نحوه‌ی عملکرد styled-compnent شده‌اید. زیرا styled-component در ساختار خود در حال استفاده از این مفاهیم و tagged template literal ها است تا کاربران بتوانند استایل‌ها را بر اساس پراپرتی‌های پاس داده شده به هر کامپوننت، تغییر دهند.

در واقع می‌توان گفت بدون tagged template literal قادر به ساخت styled-component نبوده‌ایم. شما نیز شاید بتوانید در آینده کاربردهای جدیدی از قابلیت‌های کم‌تر شناخته‌شده‌ی زبان جاوااسکریپت را پیدا کنید که بتوانند مانند styled-component فراگیر شود.

منبع

چه امتیازی به این مقاله می دید؟
خیلی بد
بد
متوسط
خوب
عالی

دیدگاه‌ها و پرسش‌ها

برای ارسال دیدگاه لازم است، ابتدا وارد سایت شوید.

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

آفلاین
user-avatar
ابوالفضل باغشاهی @BAbolfazl
Front-End
دنبال کردن

گفتگو‌ برنامه نویسان

بخشی برای حل مشکلات برنامه‌نویسی و مباحث پیرامون آن وارد شو