بهترین شیوهها عمدتاً به ترجیحات شخصی مربوط میشوند و افرادی با نظرات متفاوت میتوانند با آن ها مخالفت کنند.همانطور که گفته شد، بهترین روش ها در این مقاله بر اساس یک دهه کار با برنامه های کاربردی تک صفحه ای و مدیریت وضعیت ها است.من در 7 سال گذشته در بیش از 100 پروژه Angular حضور داشته ام و هزاران روش مختلف را دیده ام و طرز فکر صدها و صدها متخصص مختلف را یاد گرفته ام.من flux، redux، state models، @ngrx/store، Akita، Ngxs، BehaviorSubjects، ObservableState، Signals را انجام داده ام... چیزهای زیادی دیده ام و این مقاله درباره نحوه استدلال من در مورد حالت است.
مدیریت state چیست ؟
این ممکن است یک سوال پیش پا افتاده به نظر برسد اما نظرات مختلفی در مورد این موضوع وجود دارد. اولین سوالی که می خواهیم از خود بپرسیم این است: "state چیست؟". "وضعیت یک مقدار یا شی است که در حافظه نگهداری می شود".
@Component({...} export class HelloCompontent { // This message property is state public message = 'hello'; }
حالا مدیریت state چیست؟ زمانی در مورد مدیریت state صحبت خواهیم کرد که یک یا چند مورد از این گفته ها درست باشد:
اولین تصور اشتباه :
بسیاری از توسعه دهندگان فکر می کنند که مدیریت state به این معنی است که: به اشتراک گذاری وضعیت در سراسر برنامه. این درست نیست، و با این استدلال در مورد state مردم تمایل دارند تا وضعیت های زیادی را مدیریت کنند یا آن را اشتباه مدیریت کنند.
مدیریت state به سادگی مثال زیر است :
@Component({...} export class HelloCompontent { // This message property is state public message = 'hello'; // This is state management public update(newMessage: string): void { this.message = newMessage; } }
دومین تصور اشتباه :
اینکه آیا شما عاشق ایجاد اکشنها برای هر چیزی هستید که باعث ایجاد افکتها میشود، که اینها نیز باعث ایجاد افکتهای دیگری میشوند که دوباره باعث ایجاد افکتهای دیگر میشوند، تصمیمی است که هر تیم باید برای خود بگیرد.اما بیایید وانمود نکنیم که این مدیریت state است.
هنگامی که این اصل برای اکثر راه حل ها در توسعه نرم افزار (یا حتی در زندگی) معتبر است، بسیار مهم است که در مدیریت state نیز این ساده نگه داشتن را رعایت کنید.انگولار از مدیریت state مبتنی بر RxJS به سمت مدیریت حالت سیگنال محور حرکت می کند.این پیام بسیار واضحی را ارسال می کند: تیم اصلی انگولار می خواهد مدیریت حالت را ساده تر کند.
این به این خلاصه می شود: همه چیز را در یک store قرار ندهید. اگر انجام دهید:
این مثال را در نظر بگیرید،آیا این کد ساده تر از این نمی تواند باشد؟!
@Component({ ... template: ` <user-form [user]="user$|async"></user-form> ` }) export class UserComponent { ... public user$ = this.userId$.pipe( switchMap(id =>this.userService.getById(id)) ); }
این مثال ساده:
برای مثال، با قرار دادن آن در store در ngrx@ ، پیچیدگی های زیر را داریم:
قانون کلی این است: اگر حالت شما نیازی به اشتراک گذاری ندارد، آن را در store قرار ندهید.
منظور ما از آن این است که اگر به حالت خود در پایین ترین مؤلفه فرزند نیاز دارید، و فقط در آنجا... آن را همانجا نگه دارید!
انگولار دارای این ویژگی تزریق وابستگی عالی است که در آن ما می توانیم یک تزریق را در انواع مختلف سطوح ارائه کنیم. به عنوان مثال به این مثال توجه کنید:
@Component({ ... // provided right on this ChatboxComponent providers: [ChatboxState] }) export class ChatboxComponent { private readonly state = inject(ChatboxState); }
اکنون ChatboxState برای ChatboxComponent مهیا شده است و چرخه عمر آن مؤلفه را نیز به اشتراک میگذارد.این بدان معناست که وقتی ChatboxComponent از بین میرود، نمونه ChatboxState نیز از بین میرود. حتی می توانید قلاب چرخه حیات ()ngOnDestroy را در ChatboxState نیز پیاده سازی کنید:
export class ChatboxState implements OnDestroy { // gets called when the item that provides this class gets destroyed public ngOnDestroy(): void { } }
هرچه این state را پایین تر نگه دارید، مدیریت آن برای ما آسان تر خواهد شد. در این سناریو، به عنوان مثال، بی اعتباری حالت به صورت رایگان اتفاق می افتد.
طرز تفکر سالم در مورد state این است که state همیشه باید یک مقدار داشته باشد. این بدان معناست که ما همیشه نمیخواهیم subscribe کنیم، اما میخواهیم یک snapshots از آن مقدار دریافت کنیم.
این مثال بد را در نظر بگیرید:
public saveUser(): void { this.user$ .pipe( take(1), withLatestFrom(this.courses$), takeUntil(this.destroy$) ) .subscribe(({user, courses}) => { this.userService.update(user, courses).subscribe({...}) }) }
از آنجا که $user و $courses هر دو observable هستند، نمیتوانیم فقط مقادیری از آنها دریافت کنیم. برای بازیابی آن مقادیر، باید subscribe کنیم. اگر حالت ما synchronous باشد، این قطعه کد بسیار تمیزتر می شود:
public saveUser(): void { const {user, courses} = this.state.snapshot; this.userService.update(user, courses).subscribe({...}) }
آن قطعه کد از ObservableState استفاده میکند، اما سیگنالها نیز در مورد آن هستند. با سیگنال ها به شکل زیر است:
public saveUser(): void { const {user, courses} = this.state(); this.userService.update(user, courses).subscribe({...}) }
هنگام کار با state، این یک راه سالم است که همیشه به مقادیر اولیه فکر کنید: اگر می توانید از مقدار null اجتناب کنید، در برخی موارد به آن نیاز خواهید داشت، اما نمونه سازی یک آرایه با [ ] کد شما را قوی تر می کند.شما خطای "Cannot read properties of null" کمتری خواهید داشت و استدلال در مورد state را آسان تر می کند.نوشتن type برای state نیز آسان تر می شود زیرا مجبور نیستید your-type>|null>
را بعد از هر نوع حالت اضافه کنید. این برای مثال می تواند بسیار آزاردهنده باشد:
type UserState = { firstName: string|null, lastName: string|null // etc }
ساختارهای داده تغییرناپذیر لزوماً به دلیل بهینه سازی عملکرد استفاده نمی شوند. دلیل استفاده از آن این است که این ساختارها قابل پیش بینی هستند. ما میتوانیم Change Detection را بهینه کنیم، میتوانیم از عملگرهای RxJS استفاده کنیم و میتوانیم جریانهای داده یک طرفه تمیز ایجاد کنیم.
بطور خلاصه :
چه از چارچوبهای مدیریت حالت استفاده کنیم یا از پیادهسازیهای سفارشی، سعی کنید آن را ساده نگه دارید، state را تا حد امکان پایین نگه دارید، مطمئن شوید که مقادیر اولیه و snapshots دارید و وضعیتی را که نباید مدیریت کنید، مدیریت نمیکنید.
لینک مقاله اصلی: https://blog.simplified.courses/angular-state-management-best-practices