JavaScript Web Workers
شايفك بتهرش في دماغك وبتسأل نفسك سؤال مهم
إزاي JavaScript بتشتغل بسنجل thread و Web Workers بتعمل Multi-threading؟
هجاوبك بالتفصيل في الأخر
لو بتدور على طريقة تخلي كود JavaScript بتاعك أسرع وأكثر كفاءة، Web Workers ممكن يكونوا أفضل صديق ليك! النهارده هنتكلم عن مفهوم جميل زي ما يكون عندك superhero Assistant لتطبيقات الويب بتاعتك
Web Workers إيه بقى دول؟
فكر في Web Workers كحاجة بتشتغل في الخلفية، بيعملوا مهام زي الحسابات المعقدة أو معالجة البيانات من غير ما يبطئوا الـ main thread بتاعك. ده معناه إن تطبيق الويب بتاعك يفضل سلس وسريع حتى لما تكون بتتعامل مع مهام تقيلة.
ليه تتعب نفسك مع Web Workers؟
JavaScript
بيشتغل بthread واحد (single-threaded)، يعني بيعمل حاجة واحدة في الوقت الواحد.
لما تشغل مهمة طويلة على الـ main thread، تطبيق الويب بتاعك ممكن يهنج أو ياخد وقت في التحميل، ويخلي المستخدمين يستنوا ويحسوا بالإحباط تخيل تفضل تضغط ع زرار ومفيش حاجة بتحصل !!!
Web Workers
بتيجي تنقذك بإنها تتعامل مع المهام دي على thread مختلف، فالتطبيق بتاعك يفضل مستجيب، والمستخدمين يبقوا مبسوطين من السرعة.
خلينا نشمر إيدينا ونخش ف الكود شوية!
هنقسم الموضوع لخطوات بسيطة علشان تفهم إزاي بيشتغل!
الخطوة الأولي => إنشاء سكريبت الـ Worker
أول حاجة، محتاجين ملف JavaScript منفصل لل workers بتوعنا. نسميه مثلاً worker.js أو أي اسم تفضله!
بص ع الكود في صورة واحد
في الكود ده، بنجهز الـ worker علشان يستقبل الرسائل باستخدام self.onmessage، ويعمل المهام التقيلة (زي حساب رقم Fibonacci)، وبعدين يبعت النتيجة باستخدام postMessage.
ممكن تقولي إستني ي عم هو ف إيه و إيه هو self؟ أو إيه هي postMessage و onmessage؟
Web Workers
بيشتغلوا في global context مختلف، مش الـ window اللي احنا عارفينه
بنسمي الglobal context الخاص ده self!
Web Workers
بيتواصلوا مع الـ main thread باستخدام الأحداث (events).
باستخدام الأحداث، يقدروا يبعتوا ويستقبلوا رسائل أو بيانات. في الكود، onmessage عادة بيستقبل رسائل أو بيانات من الـ main thread،
وpostMessage بيبعت البيانات المعالجة من الـ Web Worker لل main thread
الخطوة التانية إنشاء والتحدث مع الـ Worker
دلوقتي، هنشوف إزاي ننشئ Worker ونبعت رسائل/بيانات ليه من السكريبت الرئيسي بتاعنا ، مستني إيه بص ع الصورة رقم 2
هنا، بننشئ worker جديد، نبعت رسالة ليه باستخدام worker.postMessage
ونستلم النتائج باستخدام worker.onmessage
كمان بنتعامل مع أي أخطاء محتملة باستخدام worker.onerror
طب م دا برضه كلام حلو بس مش كفاية أنا عاوز أشوف سيناريوهات تفيدني واقعية
لو عندك array كبيرة جدا عاوز تعملها sort
خلينا نخلي Web Worker يتعامل مع الموضوع ده بص ع الكود في صورة 3
مثال تاني واقعي أكثر
عندك api بتعملها fecth وبتغيب ؟ هييجي واحد يقولي أنا شغال Asynchronous هقوله ماشي ي حبيبي ده مبيغيرش حقيقة إن fetch بيشتغل على الـ main thread حتى لو استخدمت async/await،
كمثال ع دا بص ع الصورة رقم 4
ال Best Practices عشان تستخدم ال Web Workers
Web Workers
معندهمش وصول مباشر للـ DOM، فخلي مهامهم مركزة.
دايماً ضيف error handling علشان تتجنب المشاكل.
معظمنا بيستخدم JavaScript في المتصفحات وعارفين إنها بتشتغل بسنجل ثريد، بس إزاي Web Workers بتعمل Multi-threading؟
JavaScript
مش بتحدد موديل معين للثريدينج، لكنها بتشتغل بسنجل ثريد في معظم المتصفحات. ده يعني إن الكود بيتنفذ في ثريد واحد، وكل العمليات بتتعمل واحدة واحدة.
معظم المتصفحات القديمة كانت بتشتغل بسنجل ثريد، بس دلوقتي المتصفحات زي Chrome، Firefox، بدأت تستخدم أكتر من ثريد عشان تحسن الأداء.
Web Workers مش جزء من JavaScript نفسها، لكنها ميزة في المتصفحات بنقدر نوصلها من خلال JavaScript.
الWorkers دي بتشتغل في ثريدات منفصلة، وده بيساعد على إننا نعمل عمليات تقيلة من غير ما نبطئ الـ main thread.
المتصفح هو اللي بيتولى إدارة الthreads. لما نعمل Worker، المتصفح بيكريت ثريد جديد ويشغل الكود بتاعه فيه.
الكود اللي بيشتغل في الـ Worker بيبقى منفصل عن الـ main thread، يعني مش بيتأثر بأي عمليات تانية شغالة في نفس الوقت يعني مش بنحتاج نقلق على موضوع الـ Memory model أو الـ concurrent access للمتغيرات.
الإتصال بين الـ main thread والـ Workers بيتم من خلال الرسائل (messages). ده بيسمح للـ Workers إنها تبعت وتستقبل بيانات من غير ما تأثر على الأداء العام.
شايفك بتهرش في دماغك وبتسأل نفسك سؤال مهم
إزاي JavaScript بتشتغل بسنجل thread و Web Workers بتعمل Multi-threading؟
هجاوبك بالتفصيل في الأخر
لو بتدور على طريقة تخلي كود JavaScript بتاعك أسرع وأكثر كفاءة، Web Workers ممكن يكونوا أفضل صديق ليك! النهارده هنتكلم عن مفهوم جميل زي ما يكون عندك superhero Assistant لتطبيقات الويب بتاعتك
Web Workers إيه بقى دول؟
فكر في Web Workers كحاجة بتشتغل في الخلفية، بيعملوا مهام زي الحسابات المعقدة أو معالجة البيانات من غير ما يبطئوا الـ main thread بتاعك. ده معناه إن تطبيق الويب بتاعك يفضل سلس وسريع حتى لما تكون بتتعامل مع مهام تقيلة.
ليه تتعب نفسك مع Web Workers؟
JavaScript
بيشتغل بthread واحد (single-threaded)، يعني بيعمل حاجة واحدة في الوقت الواحد.
لما تشغل مهمة طويلة على الـ main thread، تطبيق الويب بتاعك ممكن يهنج أو ياخد وقت في التحميل، ويخلي المستخدمين يستنوا ويحسوا بالإحباط تخيل تفضل تضغط ع زرار ومفيش حاجة بتحصل !!!
Web Workers
بتيجي تنقذك بإنها تتعامل مع المهام دي على thread مختلف، فالتطبيق بتاعك يفضل مستجيب، والمستخدمين يبقوا مبسوطين من السرعة.
خلينا نشمر إيدينا ونخش ف الكود شوية!
هنقسم الموضوع لخطوات بسيطة علشان تفهم إزاي بيشتغل!
الخطوة الأولي => إنشاء سكريبت الـ Worker
أول حاجة، محتاجين ملف JavaScript منفصل لل workers بتوعنا. نسميه مثلاً worker.js أو أي اسم تفضله!
بص ع الكود في صورة واحد
في الكود ده، بنجهز الـ worker علشان يستقبل الرسائل باستخدام self.onmessage، ويعمل المهام التقيلة (زي حساب رقم Fibonacci)، وبعدين يبعت النتيجة باستخدام postMessage.
ممكن تقولي إستني ي عم هو ف إيه و إيه هو self؟ أو إيه هي postMessage و onmessage؟
Web Workers
بيشتغلوا في global context مختلف، مش الـ window اللي احنا عارفينه
بنسمي الglobal context الخاص ده self!
Web Workers
بيتواصلوا مع الـ main thread باستخدام الأحداث (events).
باستخدام الأحداث، يقدروا يبعتوا ويستقبلوا رسائل أو بيانات. في الكود، onmessage عادة بيستقبل رسائل أو بيانات من الـ main thread،
وpostMessage بيبعت البيانات المعالجة من الـ Web Worker لل main thread
الخطوة التانية إنشاء والتحدث مع الـ Worker
دلوقتي، هنشوف إزاي ننشئ Worker ونبعت رسائل/بيانات ليه من السكريبت الرئيسي بتاعنا ، مستني إيه بص ع الصورة رقم 2
هنا، بننشئ worker جديد، نبعت رسالة ليه باستخدام worker.postMessage
ونستلم النتائج باستخدام worker.onmessage
كمان بنتعامل مع أي أخطاء محتملة باستخدام worker.onerror
طب م دا برضه كلام حلو بس مش كفاية أنا عاوز أشوف سيناريوهات تفيدني واقعية
لو عندك array كبيرة جدا عاوز تعملها sort
خلينا نخلي Web Worker يتعامل مع الموضوع ده بص ع الكود في صورة 3
مثال تاني واقعي أكثر
عندك api بتعملها fecth وبتغيب ؟ هييجي واحد يقولي أنا شغال Asynchronous هقوله ماشي ي حبيبي ده مبيغيرش حقيقة إن fetch بيشتغل على الـ main thread حتى لو استخدمت async/await،
كمثال ع دا بص ع الصورة رقم 4
ال Best Practices عشان تستخدم ال Web Workers
Web Workers
معندهمش وصول مباشر للـ DOM، فخلي مهامهم مركزة.
دايماً ضيف error handling علشان تتجنب المشاكل.
معظمنا بيستخدم JavaScript في المتصفحات وعارفين إنها بتشتغل بسنجل ثريد، بس إزاي Web Workers بتعمل Multi-threading؟
JavaScript
مش بتحدد موديل معين للثريدينج، لكنها بتشتغل بسنجل ثريد في معظم المتصفحات. ده يعني إن الكود بيتنفذ في ثريد واحد، وكل العمليات بتتعمل واحدة واحدة.
معظم المتصفحات القديمة كانت بتشتغل بسنجل ثريد، بس دلوقتي المتصفحات زي Chrome، Firefox، بدأت تستخدم أكتر من ثريد عشان تحسن الأداء.
Web Workers مش جزء من JavaScript نفسها، لكنها ميزة في المتصفحات بنقدر نوصلها من خلال JavaScript.
الWorkers دي بتشتغل في ثريدات منفصلة، وده بيساعد على إننا نعمل عمليات تقيلة من غير ما نبطئ الـ main thread.
المتصفح هو اللي بيتولى إدارة الthreads. لما نعمل Worker، المتصفح بيكريت ثريد جديد ويشغل الكود بتاعه فيه.
الكود اللي بيشتغل في الـ Worker بيبقى منفصل عن الـ main thread، يعني مش بيتأثر بأي عمليات تانية شغالة في نفس الوقت يعني مش بنحتاج نقلق على موضوع الـ Memory model أو الـ concurrent access للمتغيرات.
الإتصال بين الـ main thread والـ Workers بيتم من خلال الرسائل (messages). ده بيسمح للـ Workers إنها تبعت وتستقبل بيانات من غير ما تأثر على الأداء العام.
❤2
فهم الـ Automatic Batching في React 18
في React 18، ظهرت ميزة جديدة وهي Automatic Batching، اللي بتساعد في تقليل الـ renders وبالتالي بتحسن الأداء بشكل كبير. قبل ما نتكلم عن الـ Automatic Batching، خلينا نفهم الأول إيه اللي كان بيحصل في الفيرجنز اللي فاتت من React وإيه التغيير اللي حصل؟
قبل فيرجن 18
في الفيرجنز القديمة من React، كان الـ Batching موجود، بس كان limited
الـ Batching ببساطة يعني إن React بتجمع أكتر من تحديث لل (state) في re-render واحد عشان تحسن الأداء.
ده كان بيحصل جوه الـ event handlers بس، يعني لو عملت تحديث للstate برا الـ event handler
React
كانت بتعمل re-render لكل تحديث لوحده.
في الـ Class components، كنت ممكن تعمل تحديث للstate باستخدام setState ، بس لو التحديث ده كان جوه function غير الـ event handler زي الـ Promise أو setTimeout،
React
كانت بتعمل re-render لكل تحديث لوحده.
تعال سوا نبص ع مثال عشان الدنيا تكون أوضح بص لصورة رقم 1
في المثال ده، لو عملت تحديث للstate جوه Promise،
React
كانت بتعمل re-render لكل تحديث لوحده، عشان كده لو عندك n تحديثات، هيحصل n re-renders.
مش فاهم تعال أوضحلك أكتر
setState
جوا Promise هيتعامل كـ async update، وده يعني إن الـ re-render هيحصل بعد تنفيذ كل الكود جوا الـ then.
لكن لما تحط الـ console.log بعد setState مباشرة، الكود ده هيشتغل قبل ما الـ state تتحدث، عشان كده الـ console.log('state', this.state.name)
هيطبع القيمة القديمة للـ state، اللي هي "setState" أو "sync" لو كنت دست على العنصر الأولاني
في handleClickAsync، نفس الكلام، ممكن تستخدم callback
كان عندك كام سنة رلنا عرفت ان ٍٍsetState فيها callback ، بهزر معاك بفكك يجدع :(
في React 18، جابوا ميزة الـ Automatic Batching، اللي بتخلي الـ Batching يشتغل بشكل تلقائي في كل الأماكن، مش بس جوه الـ event handlers.
دلوقتي، لو عملت تحديث للstate جوه Promise أو setTimeout أو أي حاجة تانية،
React
هتجمع كل التحديثات في re-render واحد.
بص ع صورة رقم 2 عشان تشوف مثال ع دا
في المثال ده، سواء التحديثات كانت جوه Batching أو برا،
React 18
هتعمل re-render واحد بس للتحديثات دي.
لو لسبب ما عايز تلغي الـ Batching وتخلي React تعمل re-render لكل تحديث لوحده، تقدر تستخدم flushSync
إزاي ؟ بص ع صورة رقم 3
نييجي للسؤال المهم
إيه هي ReactDOM.unstable_batchedUpdates؟
هي API كانت موجودة في React بتسمح للديفلوبرز ربنا يوعدنا جميعا ويجعلنا منهم ، متستغربش اه والله ، بإنهم يجمعوا (batch) تحديثات (state) بشكل يدوي في React. لما نقول إن التحديثات متجمعة في دفعة واحدة (batched)، معناها إن React بتجمع كل التحديثات اللي بتحصل في وقت معين وبتعمل (re-render) للـ component مرة واحدة بدل ما تعمل لكل تحديث لوحده.
قبل React 18،
React
كانت بتقوم بالـ batching بشكل تلقائي لما تكون التحديثات جاية من داخل event handlers (زي لما تدوس على زرار). لكن لو التحديثات جاية من داخل (asynchronous operations) زي Promises أو setTimeout،
React
مكنتش بتجمع التحديثات بشكل تلقائي، وده كان بيخلي الديفلوبرز يلجأوا للـ API
ReactDOM.unstable_batchedUpdates
عشان يقدروا يعملوا batching للتحديثات اللي جاية من العمليات دي.
طبعا كل دا اتغير في ريأكت 18
طب ليه ReactDOM.unstable_batchedUpdates
لسه موجودة؟
عشان تحافظ على التوافقية مع الإصدارات القديمة. بعض المشاريع اللي مبنية على الإصدارات القديمة من React ممكن تكون بتستخدم الـ API دي بشكل يدوي، وعلشان مفيش حاجة تبوظ عند التحديث للإصدار الجديد، React قررت تخلي الـ API موجودة لفترة مؤقتة.
في React 18، ظهرت ميزة جديدة وهي Automatic Batching، اللي بتساعد في تقليل الـ renders وبالتالي بتحسن الأداء بشكل كبير. قبل ما نتكلم عن الـ Automatic Batching، خلينا نفهم الأول إيه اللي كان بيحصل في الفيرجنز اللي فاتت من React وإيه التغيير اللي حصل؟
قبل فيرجن 18
في الفيرجنز القديمة من React، كان الـ Batching موجود، بس كان limited
الـ Batching ببساطة يعني إن React بتجمع أكتر من تحديث لل (state) في re-render واحد عشان تحسن الأداء.
ده كان بيحصل جوه الـ event handlers بس، يعني لو عملت تحديث للstate برا الـ event handler
React
كانت بتعمل re-render لكل تحديث لوحده.
في الـ Class components، كنت ممكن تعمل تحديث للstate باستخدام setState ، بس لو التحديث ده كان جوه function غير الـ event handler زي الـ Promise أو setTimeout،
React
كانت بتعمل re-render لكل تحديث لوحده.
تعال سوا نبص ع مثال عشان الدنيا تكون أوضح بص لصورة رقم 1
في المثال ده، لو عملت تحديث للstate جوه Promise،
React
كانت بتعمل re-render لكل تحديث لوحده، عشان كده لو عندك n تحديثات، هيحصل n re-renders.
مش فاهم تعال أوضحلك أكتر
setState
جوا Promise هيتعامل كـ async update، وده يعني إن الـ re-render هيحصل بعد تنفيذ كل الكود جوا الـ then.
لكن لما تحط الـ console.log بعد setState مباشرة، الكود ده هيشتغل قبل ما الـ state تتحدث، عشان كده الـ console.log('state', this.state.name)
هيطبع القيمة القديمة للـ state، اللي هي "setState" أو "sync" لو كنت دست على العنصر الأولاني
في handleClickAsync، نفس الكلام، ممكن تستخدم callback
كان عندك كام سنة رلنا عرفت ان ٍٍsetState فيها callback ، بهزر معاك بفكك يجدع :(
في React 18، جابوا ميزة الـ Automatic Batching، اللي بتخلي الـ Batching يشتغل بشكل تلقائي في كل الأماكن، مش بس جوه الـ event handlers.
دلوقتي، لو عملت تحديث للstate جوه Promise أو setTimeout أو أي حاجة تانية،
React
هتجمع كل التحديثات في re-render واحد.
بص ع صورة رقم 2 عشان تشوف مثال ع دا
في المثال ده، سواء التحديثات كانت جوه Batching أو برا،
React 18
هتعمل re-render واحد بس للتحديثات دي.
لو لسبب ما عايز تلغي الـ Batching وتخلي React تعمل re-render لكل تحديث لوحده، تقدر تستخدم flushSync
إزاي ؟ بص ع صورة رقم 3
نييجي للسؤال المهم
إيه هي ReactDOM.unstable_batchedUpdates؟
هي API كانت موجودة في React بتسمح للديفلوبرز ربنا يوعدنا جميعا ويجعلنا منهم ، متستغربش اه والله ، بإنهم يجمعوا (batch) تحديثات (state) بشكل يدوي في React. لما نقول إن التحديثات متجمعة في دفعة واحدة (batched)، معناها إن React بتجمع كل التحديثات اللي بتحصل في وقت معين وبتعمل (re-render) للـ component مرة واحدة بدل ما تعمل لكل تحديث لوحده.
قبل React 18،
React
كانت بتقوم بالـ batching بشكل تلقائي لما تكون التحديثات جاية من داخل event handlers (زي لما تدوس على زرار). لكن لو التحديثات جاية من داخل (asynchronous operations) زي Promises أو setTimeout،
React
مكنتش بتجمع التحديثات بشكل تلقائي، وده كان بيخلي الديفلوبرز يلجأوا للـ API
ReactDOM.unstable_batchedUpdates
عشان يقدروا يعملوا batching للتحديثات اللي جاية من العمليات دي.
طبعا كل دا اتغير في ريأكت 18
طب ليه ReactDOM.unstable_batchedUpdates
لسه موجودة؟
عشان تحافظ على التوافقية مع الإصدارات القديمة. بعض المشاريع اللي مبنية على الإصدارات القديمة من React ممكن تكون بتستخدم الـ API دي بشكل يدوي، وعلشان مفيش حاجة تبوظ عند التحديث للإصدار الجديد، React قررت تخلي الـ API موجودة لفترة مؤقتة.
❤1
https://www.youtube.com/watch?v=qT8N7yx1bMA
*shadow-form-handler*
مكتبة بناها الشادو أخونا عشان ي handle ال Form validations ف وجب نوجهله كل الدعم ونجربها
*shadow-form-handler*
مكتبة بناها الشادو أخونا عشان ي handle ال Form validations ف وجب نوجهله كل الدعم ونجربها
YouTube
بنيت مكتبة قوية للتحقق من الفورمات في JavaScript و TypeScript! 🚀 | دليل شامل
في هذا الفيديو، هنشرح بالتفصيل كل ما تحتاج معرفته عن مكتبة shadow-form-handler**، المكتبة اللي هتساعدك في معالجة والتحقق من الـForms بسهولة في **JavaScript و TypeScript. سواء كنت مطور مبتدئ أو محترف، هتلاقي كل الأدوات اللي تحتاجها لإدارة التحقق من البيانات…
❤2
Warning: Each child in a list should have a unique "key" prop
مستحيل تكون اشتغلت بريأكت ومشوفتش ال Warning ده :(
في اللحظة دي ممكن تفكر "ما هو مجرد تحذير"، لكن صدقني، الموضوع أخطر من كده. التحذير ده ممكن يكون سبب لمشاكل كبيرة في الـ application بتاعك.
إيه هو الـ key prop؟
الـ key prop بيكون هو الidentity بتاعت العنصر في الـ DOM. يعني لازم يكون unique وstatic عشان يفرق بين العناصر.
لو ما استخدمتش الـ key prop أو استخدمته غلط ف صدقني الموضوع مش بسيط
تعال بقي نبص للحوار دا من حياة الديفلوبرز ونشوف الديفلوبرز بيعملوا implement لل Keys إزاي ؟
استخدام Index of an array كـ KEY و جيس وات الحوار دا مش صح ، إستخدامك لل Index بيعتبر Anti-pattern.
وده رغم إنه أسهل طريقة للتخلص من التحذير اللي في الكونسول، لكنه زي السيف ذو الحدين.
الطريقة دي ممكن تسبب مشاكل كبيرة لو استخدمتها من غير ما تكون فاهم كويس إيه اللي بيحصل.
يعني فيه حالات مينفعش تستخدم فيها ال Index ك Keys منها :
لو ضفت عناصر جديدة في بداية أو نص الـ Array
لو عملت فلترة للـ Array
خلينا نبص على مثال في الصورة رقم 1
في الكود ده، عندنا Array ونقدر نضيف عناصر في البداية أو النهاية. لو ضفنا عنصر في النهاية، هتلاقي إن الـ Item 1 بياخد الـ Key 0 و الـ Item 2 بياخد الـ Key 1 ودا تمام تبدأ فين المشكلة ؟ لو ضفنا عنصر في بداية الـ Array، الـ Item 3 هيكون في الأول لكنه بياخد الـ Key 0 بدل ما ياخد 2.
وده ممكن يسبب مشاكل غير متوقعة في التطبيق زي إيه ؟
تخيل إنك شغال على تطبيق إدارة مهام (To-Do List) للمستخدمين، والتطبيق ده فيه قائمة مهام بتتحدث بشكل مستمر، والمستخدم بيقدر يضيف ويعدل ويحذف المهام في أي وقت.
الكود اللي عندك فيه مشكلة إنك بتستخدم index بتاع الـ Array كـ key لكل مهمة (task).
خليني أوريك إزاي ده ممكن يسبب كارثة فعلية.
المستخدم عنده القائمة التالية من المهام:
Task 1 (Key 0)
Task 2 (Key 1)
Task 3 (Key 2)
المستخدم قرر إنه يضيف مهمة جديدة في بداية القائمة (Task 4):
Task 4 (Key 0)
Task 1 (Key 1)
Task 2 (Key 2)
Task 3 (Key 3)
هنا كل حاجة ظاهرة تمام؟ المشكلة الكارثية بقي
تخيل إن المستخدم بيعدل على Task 3 (اللي كانت في الأصل في آخر القائمة، يعني Key 2) وبيكتب فيها ملاحظات مهمة جدًا للعمل بتاعه. وبعدين بيقرر يضيف مهمة جديدة في بداية القائمة. لما المستخدم يعمل كده، التطبيق بيقوم بعملية إعادة ترتيب للـ Keys:
Task 5 (Key 0)
Task 4 (Key 1)
Task 1 (Key 2)
Task 2 (Key 3)
Task 3 (Key 4)
اللي حصل هنا كارثة فعلية. ليه؟ لأن الـ Key بتاع كل مهمة اتغير، وده معناه إن الملاحظات اللي المستخدم كتبها في Task 3 اتحولت لمهمة تانية، أو حتى راحت تمامًا! السبب في ده هو إن الـ Key اللي مستخدمينه مش ثابت، وبمجرد ما بيحصل أي تغيير في ترتيب المهام، البيانات المرتبطة بالـ Key ده بتتلخبط.
عشان نتجنب المشاكل دي، ماينفعش نستخدم الـ Index كـ Key وهنبص ع حلول دلوقتي
في بعض الحالات ممكن نستخدم الـ Index كـ Key:
لو العناصر الجديدة بتضاف في نهاية الـ Array (لأن كده مش هيأثر على الـ Index بتاع العناصر القديمة).
لو الـ Array ثابتة (Static).
لو الـ Array مش بتتفلتر.
نبص ع الحلول بقي ؟
2. استخدام Unique ID من الـ Dataset
ده أفضل حل بدون شك، لأنك عندك Unique ID جاهز في البيانات.
3. توليد Unique IDs باستخدام Packages
في حالة إنك ماعندكش Unique IDs في الـ List، الأفضل إنك تولد Keys باستخدام بعض الـ Packages زي react-uuid أو uuid.
كمثال ع دا بص ع الصورة رقم 2
اتأكد دايمًا إنك بتولد الـ Keys قبل ما الـ Component يتعمله Mount (يعني في componentWillMount أو قبل ما يترندر). ده عشان تمنع توليد IDs جديدة في كل مرة الـ Component بيتعمله Render.
نصيحة أخيرة
استخدام Math.Random() مش مستحب نهائيًا عشان ممكن يتولد نفس الرقم مرتين، وده ممكن يسبب مشاكل.
مستحيل تكون اشتغلت بريأكت ومشوفتش ال Warning ده :(
في اللحظة دي ممكن تفكر "ما هو مجرد تحذير"، لكن صدقني، الموضوع أخطر من كده. التحذير ده ممكن يكون سبب لمشاكل كبيرة في الـ application بتاعك.
إيه هو الـ key prop؟
الـ key prop بيكون هو الidentity بتاعت العنصر في الـ DOM. يعني لازم يكون unique وstatic عشان يفرق بين العناصر.
لو ما استخدمتش الـ key prop أو استخدمته غلط ف صدقني الموضوع مش بسيط
تعال بقي نبص للحوار دا من حياة الديفلوبرز ونشوف الديفلوبرز بيعملوا implement لل Keys إزاي ؟
استخدام Index of an array كـ KEY و جيس وات الحوار دا مش صح ، إستخدامك لل Index بيعتبر Anti-pattern.
وده رغم إنه أسهل طريقة للتخلص من التحذير اللي في الكونسول، لكنه زي السيف ذو الحدين.
الطريقة دي ممكن تسبب مشاكل كبيرة لو استخدمتها من غير ما تكون فاهم كويس إيه اللي بيحصل.
يعني فيه حالات مينفعش تستخدم فيها ال Index ك Keys منها :
لو ضفت عناصر جديدة في بداية أو نص الـ Array
لو عملت فلترة للـ Array
خلينا نبص على مثال في الصورة رقم 1
في الكود ده، عندنا Array ونقدر نضيف عناصر في البداية أو النهاية. لو ضفنا عنصر في النهاية، هتلاقي إن الـ Item 1 بياخد الـ Key 0 و الـ Item 2 بياخد الـ Key 1 ودا تمام تبدأ فين المشكلة ؟ لو ضفنا عنصر في بداية الـ Array، الـ Item 3 هيكون في الأول لكنه بياخد الـ Key 0 بدل ما ياخد 2.
وده ممكن يسبب مشاكل غير متوقعة في التطبيق زي إيه ؟
تخيل إنك شغال على تطبيق إدارة مهام (To-Do List) للمستخدمين، والتطبيق ده فيه قائمة مهام بتتحدث بشكل مستمر، والمستخدم بيقدر يضيف ويعدل ويحذف المهام في أي وقت.
الكود اللي عندك فيه مشكلة إنك بتستخدم index بتاع الـ Array كـ key لكل مهمة (task).
خليني أوريك إزاي ده ممكن يسبب كارثة فعلية.
المستخدم عنده القائمة التالية من المهام:
Task 1 (Key 0)
Task 2 (Key 1)
Task 3 (Key 2)
المستخدم قرر إنه يضيف مهمة جديدة في بداية القائمة (Task 4):
Task 4 (Key 0)
Task 1 (Key 1)
Task 2 (Key 2)
Task 3 (Key 3)
هنا كل حاجة ظاهرة تمام؟ المشكلة الكارثية بقي
تخيل إن المستخدم بيعدل على Task 3 (اللي كانت في الأصل في آخر القائمة، يعني Key 2) وبيكتب فيها ملاحظات مهمة جدًا للعمل بتاعه. وبعدين بيقرر يضيف مهمة جديدة في بداية القائمة. لما المستخدم يعمل كده، التطبيق بيقوم بعملية إعادة ترتيب للـ Keys:
Task 5 (Key 0)
Task 4 (Key 1)
Task 1 (Key 2)
Task 2 (Key 3)
Task 3 (Key 4)
اللي حصل هنا كارثة فعلية. ليه؟ لأن الـ Key بتاع كل مهمة اتغير، وده معناه إن الملاحظات اللي المستخدم كتبها في Task 3 اتحولت لمهمة تانية، أو حتى راحت تمامًا! السبب في ده هو إن الـ Key اللي مستخدمينه مش ثابت، وبمجرد ما بيحصل أي تغيير في ترتيب المهام، البيانات المرتبطة بالـ Key ده بتتلخبط.
عشان نتجنب المشاكل دي، ماينفعش نستخدم الـ Index كـ Key وهنبص ع حلول دلوقتي
في بعض الحالات ممكن نستخدم الـ Index كـ Key:
لو العناصر الجديدة بتضاف في نهاية الـ Array (لأن كده مش هيأثر على الـ Index بتاع العناصر القديمة).
لو الـ Array ثابتة (Static).
لو الـ Array مش بتتفلتر.
نبص ع الحلول بقي ؟
2. استخدام Unique ID من الـ Dataset
ده أفضل حل بدون شك، لأنك عندك Unique ID جاهز في البيانات.
3. توليد Unique IDs باستخدام Packages
في حالة إنك ماعندكش Unique IDs في الـ List، الأفضل إنك تولد Keys باستخدام بعض الـ Packages زي react-uuid أو uuid.
كمثال ع دا بص ع الصورة رقم 2
اتأكد دايمًا إنك بتولد الـ Keys قبل ما الـ Component يتعمله Mount (يعني في componentWillMount أو قبل ما يترندر). ده عشان تمنع توليد IDs جديدة في كل مرة الـ Component بيتعمله Render.
نصيحة أخيرة
استخدام Math.Random() مش مستحب نهائيًا عشان ممكن يتولد نفس الرقم مرتين، وده ممكن يسبب مشاكل.
❤3👍1
دا مثال ع الpolyfill
لل include method
لو عاوز تعرف أكتر إيه هو ال polyfill
تقدر تقرأ المقال كامل هنا
https://whatsapp.com/channel/0029VaBfs40KAwEfBoG88Q2J/163
لل include method
لو عاوز تعرف أكتر إيه هو ال polyfill
تقدر تقرأ المقال كامل هنا
https://whatsapp.com/channel/0029VaBfs40KAwEfBoG88Q2J/163
❤2👏1
وإنت بتستخدم React Router أكيد قابلك المشكلة دي ،بشكل افتراضي، في أي تطبيق معمول بـReact Router، لما تعمل navigation من صفحة لصفحة تانية، الـscroll ما بيرجعش للأول، يعني لو كنت قافل الصفحة وانت في جزء معين منها، الصفحة التانية هتفتح وانت في نفس المكان.
في المقال ده، هنحل المشكلة دي سوا
بعد ما شفت إن الـscroll مش بيتغير، تعالوا نشوف مع بعض ليه ده بيحصل، وبعد كده نحل المشكلة.
ليه دا بيحصل؟
علشان نفهم ليه الـscroll position مبيتغيرش بشكل افتراضي بعد التنقل بين الصفحات في React Router، محتاجين نبص على طريقة عمل الـReact Router وكود الـSPA (Single Page Application).
ليه ما بيتغيرش الـscroll؟
لأن الكود ما بيحتويش على أي حاجة بتتحكم في الـscroll position. الـReact Router ببساطة بيعرض الصفحة الجديدة على نفس الوضع اللي كان فيه المستخدم قبل التنقل.
هو بيفترض إنك ممكن تكون عايز تحتفظ بمكانك على الصفحة اللي كنت فيها لما ترجع ليها. عشان كده، لما تضغط على رابط وتنتقل لصفحة جديدة، الصفحة الجديدة بتفتح بنفس المكان اللي كنت واقف فيه في الصفحة اللي قبلها.
فلو كنت مثلاً نازل تحت في الصفحة وشفت نص معين، وبعد كده انتقلت لصفحة تانية، هتلاقي إن الصفحة الجديدة فتحت وانت واقف تقريبًا في نفس المكان في الصفحة التانية، وما بترجعش لأعلى الصفحة بشكل تلقائي. ده اللي بنسميه "الحفاظ على حالة الـscroll"، وده الافتراضي في React Router.
الحل بيكون إنك تضيف كود أو تستخدم component زي ScrollRestoration عشان تتحكم في الـscroll position وتخليه يرجع لأول الصفحة بعد كل انتقال.
أول طريقة
طريقة ترجيع الـscroll باستخدام <ScrollRestoration />
React Router
بتوفر طريقة بسيطة جداً لترجيع الـscroll لمكانه الأول في كل مرة تعمل فيها انتقال بين الصفحات عن طريق استخدام الـ<ScrollRestoration /> .
بص كدا ع الكود اللي ف الصورة الأولي
في المثال دا SomePage و AnotherPage دي بتمثل الصفحات اللي المستخدم هيشوفها.
Root Component:
ده هو الـ layout الأساسي اللي كل الصفحات بترث منه. فيه Outlet اللي بيعرض مكونات الصفحات الفرعية، وفيه كمان nav اللي بيمثل قائمة الروابط للتنقل بين الصفحات. كمان، استخدمنا ScrollRestoration علشان نضمن إن الـ scroll بيتظبط تلقائيًا بعد كل انتقال.
createBrowserRouter
هنا بنعرّف ال (routes) بتاعة التطبيق. إحنا عرفنا طريقين (routes) أساسيين:
واحد لصفحة SomePage.
والتاني لصفحة AnotherPage.
ده بيخلي التطبيق يقدر يوجه المستخدم للصفحة المطلوبة بناءً على الـ URL.
RouterProvider
ده بيغلف التطبيق كله ويوفر التوجيهات (routing) اللي عرفناها علشان نقدر نستخدمها في كل مكان في التطبيق.
ScrollRestoration
هو كومبوننت من React Router بيساعد في ضبط مكان(scroll) بعد ما المستخدم ينتقل بين الصفحات.
ScrollRestoration
بيحل المشكلة دي وبيخلي السكرول يرجع لنقطة البداية (أعلى الصفحة) بعد كل انتقال.
الحل التاني بقي ؟
طريقة ترجيع الـscroll باستخدام useLayoutEffect() وscrollTo()
لو مش عايز المستخدم يشوف حركة الـscroll، ومحتاج الصفحة تتنقل لأعلاها مرة واحدة بدون أي أنيميشن، الحل ده ممكن يفيدك.
عشان نرجع الـscroll، هنستخدم الـuseLayoutEffect()
hook،
وهنستخدم الـscrollTo() عشان ننقل الصفحة لأعلاها وهنمرر القيمة "instant"
لـbehavior option عشان الأنيميشن يكون غير ملحوظ.
خلي بالك إننا بنستخدم useLayoutEffect() مش useEffect() عشان نرجع الـscroll قبل ما الصفحة الجديدة تتعرض ليه بقي ؟
useEffect()
بيتم تنفيذه بعد ما React يخلص من عملية ال (rendering) وعرض المحتوى على الشاشة.
useLayoutEffect()
بيتم تنفيذه بعد ما React يخلص من تعديل الـ DOM لكن قبل ما يحصل أي تحديثات تظهر للمستخدم على الشاشة.
منع الـ
FOUC (Flash of Unscrolled Content)
لو استخدمنا useEffect()، ممكن يحصل ما يسمى بـ FOUC، وهو إن المستخدم يشوف محتوى الصفحة الجديدة في مكان التمرير القديم للحظات قبل ما الـscroll يرجع لأعلى الصفحة.
useLayoutEffect() بيحل المشكلة دي لأنه بيتم تنفيذه في نفس اللحظة اللي بيتم فيها تعديل الـ DOM
فالمستخدم ما يلاحظش أي فرق.
كمثال ع دا بص ع الصورة رقم 2
في المقال ده، هنحل المشكلة دي سوا
بعد ما شفت إن الـscroll مش بيتغير، تعالوا نشوف مع بعض ليه ده بيحصل، وبعد كده نحل المشكلة.
ليه دا بيحصل؟
علشان نفهم ليه الـscroll position مبيتغيرش بشكل افتراضي بعد التنقل بين الصفحات في React Router، محتاجين نبص على طريقة عمل الـReact Router وكود الـSPA (Single Page Application).
ليه ما بيتغيرش الـscroll؟
لأن الكود ما بيحتويش على أي حاجة بتتحكم في الـscroll position. الـReact Router ببساطة بيعرض الصفحة الجديدة على نفس الوضع اللي كان فيه المستخدم قبل التنقل.
هو بيفترض إنك ممكن تكون عايز تحتفظ بمكانك على الصفحة اللي كنت فيها لما ترجع ليها. عشان كده، لما تضغط على رابط وتنتقل لصفحة جديدة، الصفحة الجديدة بتفتح بنفس المكان اللي كنت واقف فيه في الصفحة اللي قبلها.
فلو كنت مثلاً نازل تحت في الصفحة وشفت نص معين، وبعد كده انتقلت لصفحة تانية، هتلاقي إن الصفحة الجديدة فتحت وانت واقف تقريبًا في نفس المكان في الصفحة التانية، وما بترجعش لأعلى الصفحة بشكل تلقائي. ده اللي بنسميه "الحفاظ على حالة الـscroll"، وده الافتراضي في React Router.
الحل بيكون إنك تضيف كود أو تستخدم component زي ScrollRestoration عشان تتحكم في الـscroll position وتخليه يرجع لأول الصفحة بعد كل انتقال.
أول طريقة
طريقة ترجيع الـscroll باستخدام <ScrollRestoration />
React Router
بتوفر طريقة بسيطة جداً لترجيع الـscroll لمكانه الأول في كل مرة تعمل فيها انتقال بين الصفحات عن طريق استخدام الـ<ScrollRestoration /> .
بص كدا ع الكود اللي ف الصورة الأولي
في المثال دا SomePage و AnotherPage دي بتمثل الصفحات اللي المستخدم هيشوفها.
Root Component:
ده هو الـ layout الأساسي اللي كل الصفحات بترث منه. فيه Outlet اللي بيعرض مكونات الصفحات الفرعية، وفيه كمان nav اللي بيمثل قائمة الروابط للتنقل بين الصفحات. كمان، استخدمنا ScrollRestoration علشان نضمن إن الـ scroll بيتظبط تلقائيًا بعد كل انتقال.
createBrowserRouter
هنا بنعرّف ال (routes) بتاعة التطبيق. إحنا عرفنا طريقين (routes) أساسيين:
واحد لصفحة SomePage.
والتاني لصفحة AnotherPage.
ده بيخلي التطبيق يقدر يوجه المستخدم للصفحة المطلوبة بناءً على الـ URL.
RouterProvider
ده بيغلف التطبيق كله ويوفر التوجيهات (routing) اللي عرفناها علشان نقدر نستخدمها في كل مكان في التطبيق.
ScrollRestoration
هو كومبوننت من React Router بيساعد في ضبط مكان(scroll) بعد ما المستخدم ينتقل بين الصفحات.
ScrollRestoration
بيحل المشكلة دي وبيخلي السكرول يرجع لنقطة البداية (أعلى الصفحة) بعد كل انتقال.
الحل التاني بقي ؟
طريقة ترجيع الـscroll باستخدام useLayoutEffect() وscrollTo()
لو مش عايز المستخدم يشوف حركة الـscroll، ومحتاج الصفحة تتنقل لأعلاها مرة واحدة بدون أي أنيميشن، الحل ده ممكن يفيدك.
عشان نرجع الـscroll، هنستخدم الـuseLayoutEffect()
hook،
وهنستخدم الـscrollTo() عشان ننقل الصفحة لأعلاها وهنمرر القيمة "instant"
لـbehavior option عشان الأنيميشن يكون غير ملحوظ.
خلي بالك إننا بنستخدم useLayoutEffect() مش useEffect() عشان نرجع الـscroll قبل ما الصفحة الجديدة تتعرض ليه بقي ؟
useEffect()
بيتم تنفيذه بعد ما React يخلص من عملية ال (rendering) وعرض المحتوى على الشاشة.
useLayoutEffect()
بيتم تنفيذه بعد ما React يخلص من تعديل الـ DOM لكن قبل ما يحصل أي تحديثات تظهر للمستخدم على الشاشة.
منع الـ
FOUC (Flash of Unscrolled Content)
لو استخدمنا useEffect()، ممكن يحصل ما يسمى بـ FOUC، وهو إن المستخدم يشوف محتوى الصفحة الجديدة في مكان التمرير القديم للحظات قبل ما الـscroll يرجع لأعلى الصفحة.
useLayoutEffect() بيحل المشكلة دي لأنه بيتم تنفيذه في نفس اللحظة اللي بيتم فيها تعديل الـ DOM
فالمستخدم ما يلاحظش أي فرق.
كمثال ع دا بص ع الصورة رقم 2
❤5
https://useglass.ai/
أنا مش هتكلم ..
أنا مش هتكلم ..
useglass.ai
Glass.js
An AI copilot built for Next.js developers.
❤2🔥1