مدیریت حالت بین کامپوننتی در Svelte — آموزش Svelte (بخش سوم)

۴۳ بازدید
آخرین به‌روزرسانی: ۲۹ شهریور ۱۴۰۲
زمان مطالعه: ۶ دقیقه
مدیریت حالت بین کامپوننتی در Svelte — آموزش Svelte (بخش سوم)

در بخش قبلی (+) با روش مدیریت حالت یک کامپوننت منفرد در Svelte آشنا شدیم و دیدیم که اینکار چه قدر آسان است. در این بخش به بررسی مدیریت حالت بین کامپوننتی در Svelte می‌پردازیم. برای مطالعه بخش قبلی این مجموعه مطلب آموزشی می‌توانید روی لینک زیر کلیک کنید.

ارسال حالت با استفاده از props

نخستین راهبرد برای ارسال حالت بین کامپوننت‌های مختلف در Svelte همانند فریمورک‌های UI دیگر است و شامل استفاده از props است. هنگامی که یک کامپوننت نیاز دارد داده‌ها را با کامپوننت دیگر به اشتراک بگذارد، حالت نیز می‌تواند در درخت کامپوننت‌ها حرکت کند تا این که والد مشترکی با آن کامپوننت‌ها پیدا کند.

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

Context API

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

در این وضعیت، تکنیک دیگری را می‌توان استفاده کرد که Context API نام دارد. این روش در مواردی ایده‌آل است که چند کامپوننت بخواهند با فرزندان خود ارتباط بگیرند و نخواهند props را به اطراف ارسال کنند.

Context API دو تابع ارائه کرده است که از سوی پکیج‌های getContext و setContext در اختیار ما قرار دارند. کافی است یک شیء در Context تعیین کنیم و آن را به یک کلید انتساب دهیم:

1<script>
2import { setContext } from 'svelte'
3
4const someObject = {}
5
6setContext('someKey', someObject)
7</script>

در کامپوننت دیگر می‌توان از getContext برای بازیابی شیء انتساب یافته به یک کلید استفاده کرد:

1<script>
2import { getContext } from 'svelte'
3
4const someObject = getContext('someKey')
5</script>

بدین ترتیب تنها از getContext می‌توان برای بازیابی کلید در کامپوننتی که از setContext استفاده کرد و یا در یکی از فرزندان آن استفاده کرد.

اگر بخواهیم دو کامپوننت در دو درخت کامپوننت متفاوت با همدیگر زندگی کنند، می‌توانیم از ابزار دیگری به نام stores نیز استفاده کنیم.

استفاده از Store-های Svelte

Store-های Svelte ابزاری عالی برای مدیریت حالت اپلیکیشن در مواردی هستند که کامپوننت‌ها نیاز به صحبت کردن با همدیگر دارند و نمی‌خواهیم props-های زیادی به اطراف ارسال کنیم. ابتدا باید writable را از svelte/store ایمپورت کنید:

1import { writable } from 'svelte/store'

سپس یک متغیر store با استفاده از تابع ()writable ایجاد کنید و مقدار پیش‌فرض را به عنوان آرگومان نخست ارسال کنید:

1const username = writable('Guest')

این مقدار را می‌توان در فایل مجزایی مانند store.js قرار دارد و سپس در دو کامپوننت مختلف ایمپورت کرد. از آنجا که این فایل یک کامپوننت نیست، پسوند آن می‌تواند به جای svelte. به صورت js. باشد.

1import { writable } from 'svelte/store'
2export const username = writable('Guest')

در این صورت هر کامپوننت دیگر که این فایل را بارگذاری کند، می‌تواند به store دسترسی داشته باشد:

1<script>
2import { username } from './store.js'
3</script>

اینک مقدار این متغیر می‌تواند با استفاده از ()set به مقدار جدید انتساب یابد و مقدار جدید به عنوان آرگومان نخست ارسال شود:

1username.set('new username')

آن را می‌توان با استفاده از تابع ()update که متفاوت از ()set است، به‌روزرسانی کرد. تفاوت آن‌ها در این است که فقط مقدار جدید به آن ارسال نمی‌شود؛ بلکه یک تابع callback اجرا می‌شود که مقدار کنونی به عنوان آرگومانش ارسال می‌شود:

1const newUsername = 'new username!'
2username.update(existing => newUsername)

در این بخش می‌توان از منطق بیشتری استفاده کرد:

1username.update(existing => {
2  console.log(`Updating username from ${existing} to ${newUsername}`)
3  return newUsername
4})

برای به دست آوردن مقدار متغیر store به صورت یک بار مصرف می‌توان از تابع ()get که از سوی svelte/store اکسپورت شده استفاده کرد:

1import { writable, get } from 'svelte/store'
2export const username = writable('Guest')
3get(username) //'Guest'

برای ایجاد یک متغیر واکنشی که هر زمان مقدار store به‌روزرسانی می‌شود، آن نیز تغییر می‌یابد می‌توان به ابتدای این متغیر store یک $ اضافه کرد. به این ترتیب هر زمان که مقدار store تغییر یابد، کامپوننت مجدداً رندر می‌شود.

توجه داشته باشید که $ یک مقدار رزرو شده است و نباید آن را در مورد چیزهایی که ربطی به مقادیر store ندارند، استفاده کنید، چون موجب سردرگمی می‌شود. بنابراین اگر عادت دارید که به ابتدای ارجاع‌های DOM کاراکتر $ را اضافه کنید، در Svelte این کار را انجام ندهید.

گزینه دیگر که در صورت نیاز به اجرای منطق خاص در زمان تغییر یافتن متغیر مناسب خواهد بود، استفاده از متد ()subscribe در username است:

1username.subscribe(newValue => {
2  console.log(newValue)
3})

Svelte علاوه بر store-های writable دو نوع خاص از store نیز ارائه می‌کند که شامل Store-های readable و Store-های derived هستند.

Store-های Readable

Store-های Readable خاص هستند، زیرا نمی‌توانند از بیرون به‌روزرسانی شوند، چون متدهای ()set یا ()update ندارند. بدین ترتیب هر زمان که حالت اولیه آن‌ها تعیین شود، دیگر نمی‌توان آن‌ها را از خارج تغییر داد.

مستندات رسمی Svelte مثال جالبی را با استفاده از یک تایمر که یک تاریخ را به‌روزرسانی می‌کند عرضه کرده‌اند. می‌توان آن را تایمری تصور کرد که منابعی را از شبکه می‌گیرد و یک فراخوانی API انجام می‌دهد تا داده‌ها را از فایل‌سیستم بگیرد و یا هر کار دیگری که می‌توان به صورت دلخواه تنظیم کرد انجام می‌دهد. در این مورد به جای استفاده از ()writable برای مقداردهی اولیه متغیر store از ()readable استفاده می‌کنیم:

1import { readable } from 'svelte/store'
2export const count = readable(0)

می‌توانید پس از مقدار پیش‌فرض تابعی ارائه کنید که مسئول به‌روزرسانی آن خواهد بود. این تابع اقدام به دریافت تابع set برای تغییر دادن آن مقدار می‌کند:

1<script>
2import { readable } from 'svelte/store'
3export const count = readable(0, set => {
4  setTimeout(() => {
5    set(1)
6  }, 1000)
7})
8</script>

در این مورد ما مقدار را پس از 1 ثانیه از 0 به 1 به‌روزرسانی می‌کنیم. همچنین می‌توانید یک بازه نیز در این تابع تنظیم کنید:

1import { readable, get } from 'svelte/store'
2export const count = readable(0, set => {
3  setInterval(() => {
4    set(get(count) + 1)
5  }, 1000)
6})

می‌توانید از آن در کامپوننت دیگری به صورت زیر استفاده کنید:

1<script>
2import { count } from './store.js'
3</script>
4
5{$count}

Store-های Derived در Svlete

Store-های Derived امکان ایجاد یک مقدار Store جدید را می‌دهند که به مقدار Store موجود وابسته است. این کار با استفاده از تابع ()derived عرضه شده از سوی svelte/store میسر شده است که پارامتر نخست خود را از مقدار Store موجود می‌گیرد. همچنین پارامتر دوم تابعی است که مقدار Store را به عنوان پارامتر اول می‌گیرد:

1import { writable, derived } from 'svelte/store'
2
3export const username = writable('Guest')
4
5export const welcomeMessage = derived(username, $username => {
6  return `Welcome ${$username}`
7})
1<script>
2import { username, welcomeMessage } from './store.js'
3</script>
4
5{$username}
6{$welcomeMessage}

اسلات‌های Svelte

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

در هر کامپوننتی با استفاده از ساختار <slot /> یا <slot></slot> می‌توانید یک اسلات تعریف کنید. در ادامه یک کامپوننت Button.svelte می‌بینید که به سادگی یک تگ HTML به نام <button> پرینت می‌کند:

1<button><slot /></button>

این وضعیت برای توسعه‌دهندگان React اساساً همانند زیر است:

1<button>{props.children}</button>

هر کامپوننت که آن را ایمپورت کند، می‌تواند محتوایی که قرار است در اسلات قرار گیرد را با اضافه کردن آن به تگ‌های باز و بسته کامپوننت قرار دهد:

1<script>
2import Button from './Button.svelte'
3</script>
4
5<Button>Insert this into the slot</Button>

می‌توان یک مقدار پیش‌فرض تعریف کرد که زمانی تعریف می‌شود که اسلات پر نشده است:

1<button>
2  <slot>
3    Default text for the button
4  </slot>
5</button>

شما می‌توانید بیش از یک اسلات در کامپوننت داشته باشید و می‌توانید آن را با استفاده از «اسلات‌های نامدار» (named slots) از همدیگر متمایز سازید. تنها اسلات بی‌نام به صورت اسلات پیش‌فرض خواهد بود:

1<slot name="before" />
2<button>
3  <slot />
4</button>
5<slot name="after" />

شیوه استفاده از آن چنین است:

1<script>
2import Button from './Button.svelte'
3</script>
4
5<Button>
6  Insert this into the slot
7  <p slot="before">Add this before</p>
8  <p slot="after">Add this after</p>
9</Button>

کد فوق می‌تواند نتیجه زیر را در DOM رندر کند:

1<p slot="before">Add this before</p>
2<button>
3  Insert this into the slot
4</button>
5<p slot="after">Add this after</p>

رویدادهای چرخه عمری Svelte

هر کامپوننت در Svelte چندین رویداد چرخه عمری دارد که می‌توان آن‌ها را به دست آورد و به این وسیله کارکردهای موردنظر را پیاده‌سازی کرد. به صورت خاص رویدادهای زیر وجود دارند:

  • OnMount – پس از این که کامپوننت رندر می‌شود.
  • onDestroy – پس از این که کامپوننت تخریب می‌شود.
  • beforeUpdate – پیش از به‌روزرسانی DOM.
  • afterUpdate – پس از به‌روزرسانی DOM.

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

1<script>
2  import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte'
3</script>

یک سناریوی مشترک برای onMount، واکشی داده‌ها از منابع دیگر است. کاربرد onMount به صورت زیر است:

1<script>
2  import { onMount } from 'svelte'
3
4  onMount(async () => {
5    //do something on mount
6  })
7</script>

با متد onDestroy امکان پاکسازی داده‌ها یا توقف هر عملیاتی که ممکن است در مقداردهی اولیه کامپوننت آغاز شده باشد، مانند تایمر یا تابع‌های دوره‌ای زمان‌بندی‌شده با استفاده از setInterval وجود دارد. یک نکته خاص که باید توجه داشته باشیم این است که اگر تابعی را از onMount بازگشت دهیم، همان کارکرد onDestroy را خواهد داشت یعنی زمانی اجرا می‌شود که کامپوننت تخریب شود:

1<script>
2  import { onMount } from 'svelte'
3
4  onMount(async () => {
5    //do something on mount
6
7    return () => {
8      //do something on destroy
9    }
10  })
11</script>

در ادامه مثالی عملی از شیوه تنظیم یک تابع دوره‌ای برای اجرا در on mount می‌بینید که در نهایت حذف می‌شود:

1<script>
2  import { onMount } from 'svelte'
3
4  onMount(async () => {
5    const interval = setInterval(() => {
6      console.log('hey, just checking!')
7    }, 1000)
8
9    return () => {
10      clearInterval(interval)
11    }
12  })
13</script>

برای مطالعه بخش بعدی این سری مقالات روی لینک زیر کلیک کنید:

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

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
freecodecamp
نظر شما چیست؟

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