React.js عرض البرنامج التعليمي لإدارة الحالة

نشرت: 2022-03-11

تعد إدارة الحالة واحدة من أكبر المشكلات وأكثرها شيوعًا في تطوير الويب للواجهة الأمامية. يركز مطورو الواجهة الأمامية المستقلين مثلي باستمرار على إبقاء كائن الحالة متزامنًا مع وجهة نظره وتمثيل DOM. يمكن للمستخدمين التفاعل مع التطبيق بعدة طرق ، وهي مهمة كبيرة لتوفير انتقال نظيف من حالة إلى أخرى.

باستخدام هذا البرنامج التعليمي React.js ، تعرف على المزيد حول إدارة حالة العرض.

القليل من التاريخ

منذ وقت ليس ببعيد ، كان لتطبيقات الويب تدفق بيانات أبسط بكثير. سيرسل المتصفح طلبًا إلى الخادم ، وسيتم تنفيذ كل منطق التطبيق على الخادم ، وسيتم إرسال عرض HTML كامل إلى المتصفح لتقديمه إلى المستخدم. ستؤدي إجراءات المستخدم اللاحقة (مثل النقرات وعمليات إرسال النماذج وما إلى ذلك) إلى تشغيل نفس التدفق مرة أخرى. لا داعي للقلق على التطبيقات بشأن حالة المستخدم ويمكن إعادة إنشاء كل عرض عن طريق إرسال طلب جديد إلى الخادم.

ومع ذلك ، نمت تطبيقات الويب من حيث تعقيدها ، كما كانت طلبات المستخدم لواجهة المستخدم / UX تتقدم أيضًا. إعادة تحميل الصفحة بأكملها ، عندما يتغير جزء واحد فقط منها ، كانت غير فعالة وبطيئة. كنا بحاجة إلى تفاعل سريع وسريع وسريع الاستجابة مع تأثير فوري على واجهة المستخدم.

جاء جافا سكريبت للإنقاذ. بدأ المطورون في كتابة كميات كبيرة من التعليمات البرمجية التي تم تنفيذها في المتصفح قبل إرسال الطلب إلى الخادم. جلب jQuery أيضًا تطورات كبيرة في تطوير الويب للواجهة الأمامية ، حيث قدم إمكانات بسيطة وفعالة خارج الصندوق مثل التحقق من جانب العميل ، والنوافذ النموذجية ، ورسائل التنبيه ، والرسوم المتحركة ، وحتى تحديثات الصفحة الجزئية المستندة إلى Ajax.

فهم التعقيد

دعنا نلقي نظرة على مثال بسيط لتقييم قوة كلمة المرور. إذا كانت كلمة المرور على ما يرام ، فيجب أن يكون لمربع الإدخال حد أخضر ويجب أن يعرض رسالة لطيفة. إذا كانت كلمة المرور ضعيفة ، فيجب أن يكون لمربع الإدخال حد أحمر ويجب أن يعرض رسالة تحذير. قد نظهر أيضًا وجهًا مبتسمًا عندما تكون كلمة المرور قوية بدرجة كافية.

يوضح الكود التالي كيف يمكن القيام بذلك عن طريق معالجة DOM. يوجد الكثير من "ifs" هنا ، وليس من السهل جدًا قراءة الكود.

 if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }

كما هو موضح أعلاه ، نحتاج أولاً إلى التحقق مما إذا كان المستخدم قد قدم أي كلمة مرور على الإطلاق والتعامل مع الحالة التي يكون فيها حقل كلمة المرور فارغًا. وفي جميع الحالات ، نحتاج إلى التأكد من تحديث جميع عناصر DOM ذات الصلة بشكل صحيح. يتضمن هذا الرسالة والحدود والوجه المبتسم.

يمكن أن يكون حقل كلمة المرور الخاص بنا في إحدى الحالات الثلاث: فارغ أو ضعيف أو قوي. وكما لوحظ ، لدينا ثلاثة عناصر DOM مختلفة تتأثر بحالة حقل كلمة المرور. يؤدي التعامل مع جميع التركيبات ، والتأكد من عرض وجهة نظرنا بشكل صحيح ، إلى زيادة التعقيد السيكلومي حتى لو جزء بسيط من التعليمات البرمجية مثل هذا.

يعمل DOM في وضع الاحتفاظ ، مما يعني أنه يتذكر الحالة الحالية فقط. من أجل تعديل وجهة نظرنا ، نحتاج إلى تقديم إرشادات لكل عنصر من عناصر DOM وبرمجة الانتقال.

يمكن أن تكون انتقالات الترميز بدلاً من الحالات معقدة. يتزايد عدد الفروع والشيكات التي نحتاج إلى إجرائها في التعليمات البرمجية الخاصة بنا بشكل كبير مع عدد حالات العرض التي يجب إدارتها.

في مثالنا ، حددنا ثلاث حالات عرض ، مما أعطانا 3 * 2 = 6 انتقالات. بشكل عام ، نظرًا لحالات N ، لدينا انتقالات N * (N - 1) = N^2 - N التي سنحتاجها لنمذجة. فكر فقط في التعقيد المتزايد إذا أضفنا حالة رابعة إلى مثالنا.

عادة ما يكون هناك الكثير من التعليمات البرمجية المتعلقة بنمذجة التحولات . سيكون من الأفضل كثيرًا لو تمكنا فقط من تحديد حالات وجهة نظرنا وعدم القلق بشأن كل تفاصيل الانتقال من حالة إلى أخرى.

تقليل التعقيد

بافتراض أنه يمكننا إعلان حالة العرض بناءً على حالة النموذج ، بدلاً من ترميز الانتقال من حالة إلى أخرى بشكل صريح ، يمكن أن يكون لدينا شيء مثل هذا:

 var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }

لدينا هنا ثلاثة فروع بسيطة من الكود ، تمثل الحالات الثلاث المحتملة لتطبيقنا. نعيد فقط مواصفات العرض في كل فرع ، اعتمادًا على حالة النموذج. تمت إزالة كافة رموز معالجة DOM ؛ نحن نقدم فقط المعلومات حول ما نريد ، وليس كيفية الوصول إليه.

على الرغم من أن هذا الأسلوب يقلل بشكل كبير من تعقيد الكود ، إلا أنه يفترض أيضًا أن هناك شخصًا ما أو أي شيء آخر يتولى التعامل مع معالجة DOM الفعلية نيابةً عنا.

هذا هو المكان الذي تأتي فيه React. سوف تتأكد React من إدارة حالة العرض وتحديثها على الفور بناءً على حالة نموذج البيانات الأساسي.

رد فعل

React هي مكتبة JavaScript أنشأها Facebook. إنه مصمم للتعامل مع جزء واجهة المستخدم لتطبيقات الويب. يمكنك التفكير في الأمر على أنه V في هندسة MVC. انها مركزة جدا. لا يقدم أي افتراضات حول بقية مجموعة التكنولوجيا الخاصة بك ولا يتعامل مع أي شيء آخر غير عرض المكونات. لا يوفر أي آليات توجيه أو نماذج أو ميزات أخرى يتم تجميعها عادةً في أطر عمل أكبر. وبالتالي ، يمكنك مزجها واستخدامها مع أي مكتبة أو إطار عمل آخر تريده.

تسمح لنا React بتعريف واجهات المستخدم على أنها أشجار من مكونات مركبة. يحدد مطور React هذه المكونات عن طريق تحديد دالة تصيير تصف المكون ، في ضوء حالة الإدخال. يجب أن تكون هذه الوظيفة نقية (أي لا ينبغي أن يكون لها أي أثر جانبي أو تعتمد على أي شيء آخر غير المدخلات الصريحة).

تعرض وظيفة التقديم وصفًا للعرض ، والذي يستدعي React عنصر DOM الظاهري . فكر في الأمر على أنه كائن JavaScript مطابق لعنصر DOM المعروض.

عندما تقوم بتغيير حالة المكون ، فإنه يعيد عرض نفسه وجميع عناصره الفرعية ، ويعيد DOM الظاهري الجديد.

علاوة على ذلك ، لن تقوم React باستبدال HTML بسيط ، عند الانتقال من حالة إلى أخرى. سيجد الفرق بين الحالة السابقة والحالة الجديدة ، ويحسب مجموعة عمليات DOM الأكثر فاعلية لتنفيذ الانتقال.

حتى بدون وضع الأداء في الاعتبار ، يعد تقليل تعقيد الكود بحد ذاته أمرًا مهمًا ويسمح لنا بتركيز جهودنا على الأجزاء الفريدة والمعقدة من تطبيقنا.

للحصول على مزيد من الدقة ، هذه هي الطريقة التي سيتم بها صنع مثالنا التعليمي باستخدام React لإدارة حالات العرض.

ملاحظة: تمت كتابة نموذج التعليمات البرمجية التالي في المعالج المسبق JSX ، وهي طريقة شائعة لكتابة واجهة مستخدم تستند إلى React.

 function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body);

يعد مكون Emoji الذي يتم عرضه عندما تكون قوة كلمة المرور على ما يرام مع <Emoji value="smiley" /> مجرد مكون مخصص آخر (تمامًا مثل PasswordWithStrength ). يتم تعريفه على النحو التالي:

 var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });

React.js مقابل الآخرين

من أجل الإنصاف ، على الرغم من ذلك ، هناك أطر JavaScript أخرى من جانب العميل (مثل Ember و Angular و Knockout وغيرها) التي حلت مشكلة إدارة حالة العرض أيضًا ، بل إنها أضافت المزيد من الميزات إليها. إذن ، لماذا تريد استخدام React بدلاً من أي إطار عمل آخر؟

هناك ميزتان رئيسيتان تتمتع بهما React ، مقارنة بمعظم المكتبات الأخرى.

لا يوجد ربط البيانات

تستخدم بعض الأطر البديلة الأخرى ربط البيانات لتعيين عناصر DOM لخصائص الحالة ، وإبقائها متزامنة من خلال مراقبة تغييرات الخاصية. يسمح هذا الأسلوب بتقديم العرض مرة واحدة ، مع كل تغيير ثم يؤدي فقط إلى إجراء تعديلات على عناصر DOM المتأثرة. البدائل الأخرى تستخدم الفحص القذر ؛ أي ، بدلاً من ملاحظة تغييرات الملكية الفردية ، يقومون فقط بإجراء فرق بين الحالة السابقة والحالة الجديدة. تعتبر React أكثر تشابهًا مع النهج الأخير ، ولكن بدلاً من مقارنة الحالات ، فإنها تقارن تمثيلات العرض.

ليس لدى React أي ارتباط بالبيانات. من المتوقع أن يستدعي المطور طريقة setState ، أو يعيد تصيير المكون العلوي ، عندما تتغير الحالة. إنه يحتضن تدفقًا أحادي الاتجاه ، من الحالة إلى العرض.

يسهل اعتماد هذا المفهوم لأن المطورين عمومًا لا يفكرون في ربط البيانات. ينصب التركيز على التمثيل المرئي للبيانات. لذلك لا تحتاج إلى التفكير في الخصائص التابعة ، والتنسيق ، وربط وسوم HTML الخاصة ، وما إلى ذلك. باستخدام React ، يمكنك ببساطة إعادة تصيير المكون عندما يتغير النموذج.

لفهم الاختلاف في إدارة حالة العرض هنا ، دعنا نقارن Ember و React . سننشئ كائن person الذي سيخرج الاسم الكامل بأحرف كبيرة. بعد ثانيتين ، سنقوم بمحاكاة التغيير وتحديث العرض.

 // EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script>

أنشأنا كائنًا firstName الأول ، lastName ، والاسم fullName . نظرًا لأن Ember يراقب تغييرات الخاصية ، كان علينا تحديد أن fullName يعتمد على firstName lastName . للقيام بذلك أضفنا .property('firstName', 'lastName') عندما حددنا fullName .

بعد ثانيتين ، person.set('firstName', 'Harry'); يتم تنفيذ. أدى هذا إلى تحديث العرض وربطه.

الآن لنفعل الشيء نفسه في React.

 // EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000);

على الرغم من أن كود Ember بسيط وسهل القراءة ، فمن الواضح أن React يفوز في البساطة. person عبارة عن كائن جافا سكريبت عادي ، مع كون fullName مجرد وظيفة.

لا يوجد قالب

لكل إطار عمل بديل طريقة مختلفة للتعامل مع القوالب. يستخدم البعض سلاسل يتم تجميعها في JavaScript ، بينما يستخدم البعض الآخر عناصر DOM مباشرة. يستخدم معظمهم سمات وعلامات HTML المخصصة والتي يتم "تجميعها" بعد ذلك في HTML.

القوالب ليست جزءًا من كود JavaScript. لهذا السبب ، يحتاج كل بديل إلى طريقة مخصصة لتمثيل العمليات المشتركة ، والشرطية ، والتكرار ، ووظائف الاستدعاء ، وما إلى ذلك ، وينتهي بهم الأمر جميعًا بإنشاء لغة زائفة جديدة يجب على المطورين تعلمها.

لا توجد قوالب في React ، كل شيء مجرد جافا سكريبت قديم.

تستخدم React القوة الكاملة لجافا سكريبت لتوليد العرض. طريقة عرض المكون هي وظيفة JavaScript.

JSX متاح كمعالج أولي يحول "بناء جملة يشبه HTML" إلى JavaScript عادي ، لكن JSX اختياري وأنت حر في استخدام JavaScript القياسي دون أي معالجات أولية. يمكنك أيضًا الاستفادة من أدوات JavaScript الموجودة. Linters والمعالجات الأولية ونوع التعليقات التوضيحية والتصغير وإزالة الشفرة الميتة وما إلى ذلك.

دعنا مرة أخرى نستخدم مثالًا ملموسًا لمقارنة React بأحد الأطر البديلة لإدارة حالة العرض.

البرنامج التعليمي التالي هو مثال على استخدام AngularJS لسرد علامات التجزئة وعدد التغريدات لكل منها. يتم فرز القائمة حسب العدد ، وتظهر رسالة في حالة عدم وجود علامات تصنيف.

 <!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div>

لتكون قادرًا على إنشاء هذه القائمة ، يجب أن يتعرف المطور على AngularJS directives و ng-show و ng-repeat . ثم يحتاج إلى التعرف على AngularJS filters orderBy لفهم الترتيب حسب. الكثير من العمل لشيء بسيط مثل إخراج قائمة!

الآن دعنا نفكر في مثال React الذي يفعل نفس الشيء:

 // EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } }

حتى إذا استخدمنا الأسلوب "الأكثر تقدمًا" و JSX ، يمكن لكل مطور ويب لديه فهم أساسي لجافا سكريبت قراءة الكود أعلاه وفهم ما يفعله بسهولة. الفحص الشرطي القياسي باستخدام if ، التكرار باستخدام map() و "sort ()" القياسي يأتي بشكل طبيعي إلى أي مطور ، لذلك لا توجد صياغة إضافية أو مفاهيم أخرى للتعلم.

خاتمة

الخلاصة الرئيسية من هذا البرنامج التعليمي React.js هي حقيقة أن React يمكّنك من التركيز على إدارة حالة العرض الفعلية بدلاً من الانتقالات ، وبالتالي تبسيط عملك وتطبيقك.

منحنى التعلم لاعتماد React تافه إلى حد ما. لا توجد لغة قوالب مخصصة لإتقانها ، ولا داعي للقلق بشأن ربط البيانات ، وكل شيء يعود إلى وظائف JavaScript التي تصف عناصر واجهة المستخدم.

لمعرفة المزيد حول تبسيط كود التطبيق الخاص بك باستخدام React ، ألق نظرة على هذا الحديث بواسطة Steven Luscher ، Decomplexifying Code with React.

إليك بعض القراءة الإضافية لمن يريد اتخاذ الخطوة التالية والبدء في استخدام React:

  • http://jlongster.com/Removing-User-Interface-Complexity،-or-Why-React-is-Awesome