برنامه نویسی

جاسازی داده ها در عناصر React/JSX

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

این تکنیک برای استخراج بیت هایی از داده ها طراحی شده است که در یک عنصر JSX (یا… HTML ساده) جاسازی شده اند. چرا باید این کار را انجام دهید؟ خب… خوشحالم که پرسیدی.

سناریو

بیایید به یک تابع واقعاً اساسی نگاه کنیم:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td key={`cell-${rowIndex}-${cellIndex}`}>
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این تابع به سادگی یک ردیف خاص از سلول های جدول می سازد. در برنامه https://paintmap.studio من برای ساختن یک “نقشه رنگی” استفاده می شود. این یک شبکه (جدول) غول پیکر ایجاد می کند که به من نشان می دهد، برای هر بلوک در شبکه، کدام یک از رنگ های من بیشتر با آن بلوک خاص مطابقت دارد.

هنگامی که این ویژگی ساخته شد، تصمیم گرفتم که می خواهم یک ویژگی اضافه کنم onClick رویداد برای هر سلول ایده این است که، وقتی روی هر سلولی کلیک می‌کنید، هر سلولی را در شبکه که رنگی مشابه آن چیزی که روی آن کلیک کرده‌اید، برجسته می‌کند.

هر زمان که روی یک سلول کلیک کنید، onClick مدیریت رویداد باید درک کند که رنگی که انتخاب کرده اید به عبارت دیگر، شما نیاز دارید عبور رنگ به را onClick کنترل کننده رویداد بارها و بارها، من کدی را می بینم که به نظر می رسد برای انجام چنین عملکردی:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={paintIndex => handleCellClick(paintIndex)}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد بالا “اشتباه” نیست. عبور خواهد کرد paintIndex به مسئول رویداد اما واقعا اینطور نیست… بهینه. ناکارآمدی که به وجود می آید این است که برای هر سلول جدول، یک تعریف تابع کاملاً جدید ایجاد می کنیم. این چیزی است که paintIndex => handleCellClick(paintIndex) میکند. این یک عملکرد کاملاً جدید را می چرخاند. اگر میز بزرگی دارید، این تعاریف تابع لوتا است. و این توابع نه فقط در رندر اولیه کامپوننت، بلکه باید دوباره تعریف شوند هر زمان که این جزء دوباره فراخوانی شود.

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

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در کد بالا، handleCellClick قبلاً خارج از این تابع تعریف شده است. بنابراین React نیازی به بازسازی یک تعریف تابع کاملاً جدید در هر بار که یک سلول جدول را ارائه می کنیم، ندارد. متأسفانه، این نیز کاملاً کار نمی کند. زیرا اکنون، هر بار که کاربر روی یک سلول جدول کلیک می کند، کنترل کننده رویداد هیچ ایده ای نخواهد داشت خاص سلول کلیک شد. بنابراین آن را نمی داند که paintIndex برجسته کردن

باز هم به روش من به طور معمول مشاهده این پیاده سازی، حتی در پایگاه های کد به خوبی ساخته شده، استفاده از paintIndex => handleCellClick(paintIndex) رویکرد. اما همانطور که قبلاً اشاره کردم، این ناکارآمد است.

پس بیایید به چند راه برای رفع این مشکل نگاه کنیم.

توضیحات تصویر

اجزای لفاف

یک روش این است که یک جزء بسته بندی برای سلول های جدول من ایجاد کنم. که شبیه این خواهد بود:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <MyTableCell
            key={`cell-${rowIndex}-${cellIndex}`}
            paintIndex={paintIndex}
         >
            {paintIndex}
         </MyTableCell>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این سناریو، ما دیگر از ویژگی پایه HTML استفاده نمی کنیم <td>. در عوض، یک جزء سفارشی وجود دارد که می پذیرد paintIndex به عنوان یک تکیه گاه، و سپس احتمالاً از آن پایه برای ساخت کنترلر رویداد استفاده کنید. MyTableCell چیزی شبیه به این خواهد بود:

const MyTableCell = paintIndex => {
   const handleCellClick = () => {
      // event handler logic using paintIndex
   }

   return (
      <td onClick={handleCellClick}>
         {paintIndex}
      </td>
   )
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با استفاده از این رویکرد، ما مجبور نیستیم از آن عبور کنیم paintIndex ارزش به درون handleCellClick کنترل کننده رویداد، زیرا ما به سادگی می توانیم آن را از پایه رجوع کنیم. در این راه حل چیزهای زیادی برای دوست داشتن وجود دارد زیرا با رویکرد اصطلاحی React سازگار است. در حالت ایده آل، شما حتی می خواهید حفظ کردن را MyTableCell کامپوننت تا دوباره سوار نشود (و دوباره ارائه شده است) هر بار که سلول جدولی می سازیم که از همان رنگ رنگ استفاده می کند.

با این حال، این رویکرد همچنین می تواند کمی سخت به نظر برسد، زیرا ما جزء دیگری را صرفاً به خاطر ساختن آن استفاده می کنیم. onClick رویداد کارآمدتر همچنین، اگر handleCellClick کنترل کننده رویداد باید منطق دیگری را انجام دهد که بر آن حالت تأثیر می گذارد در مولفه فراخوان، کد حاصل می تواند کمی “سنگین” شود.

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

توضیحات تصویر

ویژگی های HTML

HTML به ما آزادی زیادی می‌دهد تا داده‌ها را در جایی که مورد نیاز است، قرار دهیم. به عنوان مثال، شما می توانید استفاده کنید longdesc ویژگی برای جاسازی paintIndex مستقیماً به خود عنصر HTML. متاسفانه، longdesc فقط “مجاز” است <frame>، <iframe>، و <img> عناصر.

مسلماً، مرورگرها در مورد استفاده از ویژگی های HTML بسیار بخشنده هستند. بنابراین اگر قرار بود شروع به گذاشتن کنید longdesc ویژگی های همه انواع عناصر HTML “غیرقانونی”، واقعا اینطور نخواهد بود زنگ تفريح هر چیزی. مرورگر اساسا فقط چشم پوشی صفات غیر اصطلاحی در واقع، شما حتی می توانید خود را اضافه کنید سفارشی ویژگی های عناصر HTML

با این وجود، معمولاً تمرین خوبی است که از قرار دادن ویژگی‌های غیرمجاز یا کاملاً سفارشی در عناصر HTML خودداری کنید. اما ما گزینه های بیشتری داریم. گزینه های “استاندارد” بیشتر.

(نزدیک) ویژگی های جهانی HTML

اگر می‌خواهید مشخصه‌ای پیدا کنید که بتوانید تقریباً روی هر عنصری قرار دهید، اولین چیز این است که به ویژگی‌هایی که در (تقریبا) هر عنصر مجاز هستند نگاه کنید. آنها به شرح زیر است:

  • id
  • class
  • style
  • title
  • dir
  • lang / xml:lang

نکته خوب در مورد این ویژگی ها این است که تقریباً می توانید از آنها در هر جایی از آن استفاده کنید بدن از HTML شما، تقریباً روی هر عنصر HTML، و شما لازم نیست نگران باشید که آیا آنها “مجاز” هستند یا خیر. برای مثال می توانید یک را قرار دهید title صفت بر روی الف <div>، یا الف dir صفت بر روی الف <td>. این همه “قابل قبول” است – با استانداردهای HTML، یعنی.

بنابراین اگر می‌خواهید از یکی از این ویژگی‌ها برای “انتقال” داده‌ها به یک مدیریت رویداد استفاده کنید، بهترین انتخاب کدام خواهد بود؟

title

اول از همه، به همان اندازه که ممکن است استفاده از چیزی شبیه وسوسه انگیز باشد title، من می خواهم نه این را توصیه کنید title توسط صفحه‌خوان‌ها استفاده می‌شود و اگر مجموعه‌ای از داده‌های برنامه‌نویسی را در آن ویژگی قرار دهید، دسترسی به سایت خود را افزایش می‌دهید – داده‌هایی که باید نه توسط یک صفحه خوان خوانده شود.

dir، lang، xml:lang

به طور مشابه، شما باید از تصاحب آن اجتناب کنید dir، lang، یا xml:lang ویژگی های. استفاده از این ویژگی ها می تواند ابزار سایت را برای کاربران بین المللی (یعنی کسانی که از سایت شما استفاده می کنند) افزایش دهد. با زبانی متفاوت). پس لطفا آنها را هم تنها بگذارید.

style

همچنین، می توانید سعی کنید داده های “سفارشی” را در یک جمع کنید style صفت. اما IMHO، به نظر پیچیده به نظر می رسد. در تئوری، می توانید یک ویژگی سبک سفارشی را مانند این تعریف کنید:

<td style={{paintIndex: `${paintIndex}`}}>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس می توانید سعی کنید این ویژگی سبک ساخته شده را در زمانی که عنصر در کنترل کننده رویداد ثبت می شود، بخوانید. اما… من چنین رویکردی را توصیه نمی کنم. اصلا. اولا اگر دارید مشروع style ویژگی های تنظیم شده بر روی عنصر، شما در نهایت با یک مخلوط از واقعی و ساخته شده. مصنوعی خواص دوم، هیچ دلیلی برای جاسازی داده ها در چنین قالب پرمخاطبی وجود ندارد. شما می توانید این کار را با گزینه های دیگر در اختیار ما بسیار “تمیزتر” انجام دهید.

id

این id ویژگی می تواند مکانی عالی برای “جاسازی” داده ها باشد. در اینجا به نظر می رسد:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            id={paintIndex}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا، ما از paintIndex به عنوان id. چرا این کار را انجام دهیم؟ زیرا در این صورت می‌توانیم یک مدیریت رویداد ایجاد کنیم که به شکل زیر باشد:

const handleCellClick = (event = {}) => {
   uiState.toggleHighlightedColor(event.target.id);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کار به این دلیل کار می‌کند که رویداد مصنوعی که به کنترل کننده رویداد منتقل می‌شود دارای این است id عنصر کلیک شده که در آن جاسازی شده است. این به ما اجازه می دهد تا از یک کنترل کننده رویداد عمومی در هر سلول جدول استفاده کنیم، در حالی که به کنترل کننده رویداد اجازه می دهد دقیقاً درک کند. که paintIndex روی آن کلیک شد.

این هنوز هم می تواند معایبی داشته باشد. اول از همه، ids قرار است باشد منحصر بفرد. در مثال بالا، یک مورد داده شده است paintIndex ممکن است در یک سلول جدول وجود داشته باشد – یا در صدها مورد. و اگر به سادگی از آن استفاده کنیم paintIndex ارزش به عنوان id، در نهایت به سلول های جدول زیادی خواهیم رسید که دارای یکسان هستند id ارزش های. (برای واضح بودن، داشتن نسخه تکراری ids HTML شما را خراب نمی کند نمایش دادن. اما در برخی سناریوها می تواند جاوا اسکریپت شما را خراب کند منطق.)

خوشبختانه، ما می توانیم آن را نیز برطرف کنیم. توجه داشته باشید که سلول های جدول ما دارند key ارزش های. و کلیدها باید منحصر به فرد باشد در این سناریو، من این مشکل را با استفاده از تعداد ردیف/سلول برای ساخت کلید برطرف کردم. زیرا هیچ دو سلولی ترکیب یکسانی از شماره ردیف/سلول را نخواهد داشت. ما می توانیم همان قالب را به خود اضافه کنیم id. به نظر می رسد این است:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            id={`cell-${rowIndex}-${cellIndex}-${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در حال حاضر، هرگز تکراری وجود نخواهد داشت id روی هر یک از سلول های ما اما ما هنوز آن را داریم paintIndex ارزش تعبیه شده به که id. بنابراین چگونه می‌توانیم مقدار را در مدیریت رویداد خود استخراج کنیم؟ به نظر می رسد این است:

const handleCellClick = (event = {}) => {
   const paintIndex = event.target.id.split('-').pop();
   uiState.toggleHighlightedColor(paintIndex);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از آنجایی که ما این کد را نوشتیم، و از زمانی که قرارداد نامگذاری را تعیین کردیم id، ما همچنین می دانیم که paintIndex value آخرین مقدار در رشته ای از مقادیر خواهد بود که با آن محدود شده اند -. اگر ما split('-') آن رشته و سپس pop() آخرین مقدار از پایان آن، ما می دانیم که در حال دریافت آن هستیم paintIndex ارزش.

class

class همچنین یک مکان عالی برای “جاسازی” داده است – حتی اگر به هیچ کلاس CSS که در دسترس اسکریپت است نگاشت نشود. اگر با jQuery UI آشنا هستید، احتمالاً موارد زیادی را دیده اید class به عنوان یک نوع “سوئیچ” استفاده می شود که در واقع سبک های CSS را هدایت نمی کند. در عوض، کد جاوا اسکریپت را می گوید چه باید کرد.

البته در JSX ما استفاده نمی کنیم class. ما استفاده می کنیم className. بنابراین آن راه حل به شکل زیر خواهد بود:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و کنترل کننده رویداد به شکل زیر است:

const handleCellClick = (event = {}) => {
   uiState.toggleHighlightedColor(event.target.className);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

درست همانطور که قبلا چنگ زدیم paintIndex از شی رویداد id میدان، ما اکنون آن را از آن می گیریم className. اگر کلاس‌های CSS “واقعی” را نیز در آن داشته باشید، چگونه کار می‌کند className ویژگی؟ که شبیه این خواهد بود:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell ${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و کنترل کننده رویداد به شکل زیر است:

const handleCellClick = (event = {}) => {
   const paintIndex = event.target.className.split(' ').pop();
   uiState.toggleHighlightedColor(paintIndex);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کلاس “سلول” در <td> یک کلاس “واقعی” است – به این معنی که به ویژگی های CSS از پیش تعریف شده نگاشت می شود. اما ما آن را نیز تعبیه کردیم paintIndex ارزش به className ویژگی و ما آن را با تقسیم رشته در فضاهای خالی استخراج کردیم.

انصافاً، این رویکرد ممکن است کمی احساس شود… “شکننده”. چون بستگی به paintIndex مقدار آخرین مقدار در فضای محدود شده است className رشته اگر توسعه دهنده دیگری وارد شد و کلاس CSS دیگری را به انتهای آن اضافه کرد className زمینه، مانند این:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell ${paintIndex} anotherCSSClass`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

منطق خواهد بود زنگ تفريح. زیرا کنترل کننده رویداد می‌گیرد anotherCSSClass از انتهای رشته خارج شوید – و سعی کنید با آن مانند آن رفتار کنید paintIndex. اگر می خواهید آن را کمی قوی تر کنید، می توانید منطق را به چیزی شبیه به این تغییر دهید:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell paintIndex-${paintIndex} anotherCSSClass`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و سپس کنترل کننده رویداد را به صورت زیر به روز کنید:

const handleCellClick = (event = {}) => {
   const paintIndex = event.target.className
      .split(' ')
      .find(className => className.includes('paintIndex-'))
      .split('-')
      .pop();
   uiState.toggleHighlightedColor(paintIndex);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با انجام این کار، مقداری که برای آن استخراج شده است paintIndex وابسته به بودن نیست آخر مورد در رشته با فاصله محدود. این می تواند در هر جایی در داخل وجود داشته باشد className ویژگی، تا زمانی که با “paintIndex-” اضافه شده باشد.

توضیحات تصویر

چرا باید اهمیت دهید؟

صادقانه بگویم، در برنامه های کوچک، داشتن چنین چیزی دقیقاً یک جرم فدرال نیست:

const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={paintIndex => handleCellClick(paintIndex)}
         >
            {paintIndex}
         </td>
      );
   })
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

هنگامی که کنترل کننده رویداد نیازی به ارسال اطلاعات از عنصر کلیک شده به آن نداشته باشد، دور نگه داشتن توابع پیکان از ویژگی های رویداد شما کار بیهوده ای است. اما زمانی که آن را میکند به اطلاعات خاص عنصر نیاز دارد، اغلب من می بینم که افراد کورکورانه به روش آسان رها کردن توابع فلش در ویژگی های خود باز می خورند. اما راه های زیادی برای جلوگیری از این امر وجود دارد – و آنها به تلاش اضافی کمی نیاز دارند.

نوشته های مشابه

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

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

همچنین ببینید
بستن
دکمه بازگشت به بالا