مدیریت حالت بین کامپوننتی در 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 را به عنوان پارامتر اول میگیرد: