خطاطيف التفاعل المفيدة التي يمكنك استخدامها في مشاريعك

نشرت: 2022-03-10
ملخص سريع ↬ المكونات المستندة إلى فئة التفاعل فوضوية ومربكة وصعبة على البشر والآلات. ولكن قبل React 16.8 ، كانت المكونات المستندة إلى الفئة إلزامية لأي مشروع يتطلب حالات وطرق دورة الحياة والعديد من الوظائف المهمة الأخرى. تغير كل هذا مع إدخال الخطافات في React 16.8. الخطافات تغير قواعد اللعبة. لقد قاموا بتبسيط React ، وجعلها أكثر إتقانًا ، وأسهل في الكتابة والتصحيح ، وقللوا أيضًا من منحنى التعلم.

الخطافات هي ببساطة وظائف تسمح لك بالربط أو الاستفادة من ميزات React. تم تقديمها في React Conf 2018 لمعالجة ثلاث مشاكل رئيسية لمكونات الفصل: غلاف الجحيم ، والمكونات الضخمة ، والفئات المربكة. تمنح الخطافات القوة للمكونات الوظيفية React ، مما يجعل من الممكن تطوير تطبيق كامل معها.

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

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

ملاحظة: هذا البرنامج التعليمي يتطلب فهمًا أساسيًا لـ Javascript (ES6 +) و React.

المزيد بعد القفز! أكمل القراءة أدناه ↓

الدافع وراء الخطافات

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

غلاف الجحيم

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

من السهل مشاركة الخطافات ، ليس عليك تعديل مكوناتك قبل إعادة استخدام المنطق.

وخير مثال على ذلك هو استخدام مكون الطلب العالي (HOC) لإعادة connect للاشتراك في متجر Redux. مثل جميع المكوّنات ذات الترتيب الأعلى ، لاستخدام الوصلة ذات الترتيب الأعلى ، يجب عليك تصدير المكون جنبًا إلى جنب مع وظائف الترتيب الأعلى المحددة. في حالة connect ، سيكون لدينا شيء من هذا القبيل.

 export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

حيث يتم تعريف وظائف mapStateToProps و mapDispatchToProps .

بينما في عصر الخطافات ، يمكن للمرء بسهولة تحقيق نفس النتيجة بدقة وإيجاز باستخدام خطافات useDispatch useSelector

مكونات ضخمة

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

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

فئات مربكة

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

يحل React هذه المشكلة بالمكونات الوظيفية والخطافات ، مما يسمح للمطورين بالتركيز على المشروع بدلاً من بناء جملة التعليمات البرمجية.

على سبيل المثال ، سوف ينتج عن مكوني React التاليين نفس النتيجة تمامًا.

 import React, { Component } from "react"; export default class App extends Component { constructor(props) { super(props); this.state = { num: 0 }; this.incrementNumber = this.incrementNumber.bind(this); } incrementNumber() { this.setState({ num: this.state.num + 1 }); } render() { return ( <div> <h1>{this.state.num}</h1> <button onClick={this.incrementNumber}>Increment</button> </div> ); } }
 import React, { useState } from "react"; export default function App() { const [num, setNum] = useState(0); function incrementNumber() { setNum(num + 1); } return ( <div> <h1>{num}</h1> <button onClick={incrementNumber}>Increment</button> </div> ); }

المثال الأول هو مكون قائم على الفصل بينما الثاني مكون وظيفي. على الرغم من أن هذا مثال بسيط ، لاحظ كيف تتم مقارنة المثال الأول الزائف بالمثال الثاني.

اتفاقية هوكس وقواعدها

قبل الخوض في الخطافات المختلفة ، قد يكون من المفيد إلقاء نظرة على الاتفاقية والقواعد التي تنطبق عليها. فيما يلي بعض القواعد التي تنطبق على الخطافات.

  1. يجب أن يبدأ اصطلاح تسمية الخطافات use البادئة. لذلك ، يمكن أن يكون لدينا useState ، و useEffect ، وما إلى ذلك. إذا كنت تستخدم برامج تحرير كود حديثة مثل Atom و VSCode ، يمكن أن يكون المكون الإضافي ESLint ميزة مفيدة للغاية لخطافات React. يوفر المكون الإضافي تحذيرات وتلميحات مفيدة حول أفضل الممارسات.
  2. يجب استدعاء الخطافات في المستوى العلوي للمكون ، قبل تعليمة الإرجاع. لا يمكن استدعاؤها داخل عبارة شرطية أو حلقة أو وظائف متداخلة.
  3. يجب استدعاء الخطافات من دالة React (داخل مكون React أو خطاف آخر). لا ينبغي استدعاؤها من وظيفة Vanilla JS.

خطاف useState

يعتبر خطاف useState هو خطاف React الأساسي والأكثر فائدة. مثل الخطافات المضمنة الأخرى ، يجب استيراد هذا الخطاف من react لاستخدامه في تطبيقنا.

 import {useState} from 'react'

لتهيئة الحالة ، يجب أن نعلن كل من الحالة ووظيفة المُحدِث الخاصة بها وتمرير قيمة أولية.

 const [state, updaterFn] = useState('')

نحن أحرار في استدعاء وظيفة الحالة والمُحدِث لدينا ما نريد ولكن وفقًا للاتفاقية ، سيكون العنصر الأول من المصفوفة حالتنا بينما سيكون العنصر الثاني هو وظيفة المُحدِث. من الممارسات الشائعة أن نبدأ وظيفة التحديث لدينا مع مجموعة البادئة متبوعة باسم حالتنا في شكل حالة الجمل.

على سبيل المثال ، دعنا نضع حالة للاحتفاظ بقيم العد.

 const [count, setCount] = useState(0)

لاحظ أن القيمة الأولية لحالة count الخاصة بنا مضبوطة على 0 وليست سلسلة فارغة. بعبارة أخرى ، يمكننا تهيئة حالتنا لأي نوع من متغيرات جافا سكريبت ، مثل العدد والسلسلة والمنطقية والمصفوفة والكائن وحتى BigInt. هناك فرق واضح بين حالات الإعداد التي تحتوي على خطاف useState وحالات المكون المستندة إلى الفئة. من الجدير بالذكر أن الخطاف useState يُرجع مصفوفة ، تُعرف أيضًا باسم متغيرات الحالة وفي المثال أعلاه ، قمنا بتدمير المصفوفة وتحويلها إلى state ووظيفة updater .

إعادة عرض المكونات

يؤدي ضبط الحالات باستخدام الخطاف useState إلى عرض المكون المقابل. ومع ذلك ، لا يحدث هذا إلا إذا اكتشفت React اختلافًا بين الحالة السابقة أو القديمة والحالة الجديدة. تقوم React بمقارنة الحالة باستخدام خوارزمية Javascript Object.is .

ضبط الدول مع useState

يمكن تعيين حالة count الخاصة بنا على قيم الحالة الجديدة ببساطة عن طريق تمرير القيمة الجديدة إلى وظيفة محدث setCount على النحو التالي setCount(newValue) .

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

بافتراض أننا نريد إضافة 5 إلى متغير count الخاص بنا في أي وقت يتم فيه النقر على زر ، يمكننا القيام بما يلي.

 import {useState} from 'react' const CountExample = () => { // initialize our count state const [count, setCount] = useState(0) // add 5 to to the count previous state const handleClick = () =>{ setCount(prevCount => prevCount + 5) } return( <div> <h1>{count} </h1> <button onClick={handleClick}>Add Five</button> </div> ) } export default CountExample

في الكود أعلاه ، قمنا أولاً باستيراد خطاف useState من react ثم قمنا بتهيئة حالة count بقيمة افتراضية 0. أنشأنا معالج onClick لزيادة قيمة count بمقدار 5 كلما تم النقر فوق الزر. ثم عرضنا النتيجة في علامة h1 .

ضبط المصفوفات وحالات الكائن

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

يتم استخدام عامل التشغيل في Javascript لإنشاء كائن جديد من كائن موجود بالفعل. هذا مفيد هنا لأن React تقارن الحالات بالعملية Object.is ثم تُعيد عرضها وفقًا لذلك.

دعنا نفكر في الكود أدناه لتحديد الحالات عند النقر على الزر.

 import {useState} from 'react' const StateExample = () => { //initialize our array and object states const [arr, setArr] = useState([2, 4]) const [obj, setObj] = useState({num: 1, name: 'Desmond'}) // set arr to the new array values const handleArrClick = () =>{ const newArr = [1, 5, 7] setArr([...arr, ...newArr]) } // set obj to the new object values const handleObjClick = () =>{ const newObj = {name: 'Ifeanyi', age: 25} setObj({...obj, ...newObj}) } return( <div> <button onClick ={handleArrClick}>Set Array State</button> <button onClick ={handleObjClick}>Set Object State</button> </div> ) } export default StateExample

في الكود أعلاه ، أنشأنا حالتين arr و obj ، وقمنا بتهيئتهما لبعض قيم الصفيف والكائن على التوالي. ثم أنشأنا معالجات onClick تسمى handleArrClick و handleObjClick لتعيين حالات المصفوفة والكائن على التوالي. عند حرائق handleArrClick ، ​​نسمي setArr ونستخدم عامل انتشار ES6 لنشر قيم الصفيف الموجودة بالفعل وإضافة newArr إليها.

فعلنا نفس الشيء لمعالج handleObjClick . هنا قمنا بتسمية setObj ، ونشر قيم الكائن الحالية باستخدام عامل انتشار ES6 ، وقمنا بتحديث قيم name age .

غير متزامن طبيعة useState

كما رأينا بالفعل ، قمنا بتعيين حالات باستخدام useState عن طريق تمرير قيمة جديدة إلى وظيفة المُحدِّث. إذا تم استدعاء المحدث عدة مرات ، فستتم إضافة القيم الجديدة إلى قائمة الانتظار وتتم إعادة العرض وفقًا لذلك باستخدام مقارنة JavaScript Object.is .

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

دعنا نفكر في المثال التالي لملاحظة هذا السلوك.

في الكود أعلاه ، أنشأنا حالة count باستخدام الخطاف useState . قمنا بعد ذلك بإنشاء معالج onClick لزيادة حالة count كلما تم النقر فوق الزر. لاحظ أنه على الرغم من زيادة حالة count ، كما هو معروض في علامة h2 ، لا تزال الحالة السابقة مسجلة في وحدة التحكم. هذا بسبب الطبيعة غير المتزامنة للخطاف.

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

هنا ، قمنا بتخزين newCountValue الذي تم إنشاؤه لتخزين قيمة العد المحدثة ثم تعيين حالة count بالقيمة المحدثة. بعد ذلك ، قمنا بتسجيل قيمة العد المحدثة في وحدة التحكم.

خطاف useEffect

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

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

النظر في المثال أدناه.

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ console.log(count) }) return( <div> ... </div> ) }

في الكود أعلاه ، قمنا ببساطة بتسجيل count في useEffect . سيتم تشغيل هذا بعد كل تصيير للمكون.

في بعض الأحيان ، قد نرغب في تشغيل الخطاف مرة واحدة (على التركيب) في مكوننا. يمكننا تحقيق ذلك من خلال توفير معلمة ثانية لخطاف useEffect .

 import {useState, useEffect} from 'react' const App = () =>{ const [count, setCount] = useState(0) useEffect(() =>{ setCount(count + 1) }, []) return( <div> <h1>{count}</h1> ... </div> ) }

يحتوي الخطاف useEffect على معلمتين ، المعلمة الأولى هي الوظيفة التي نريد تشغيلها بينما المعلمة الثانية عبارة عن مصفوفة من التبعيات. إذا لم يتم توفير المعلمة الثانية ، فسيتم تشغيل الخطاف باستمرار.

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

يمكننا أيضًا أن نجعل تأثيرنا الجانبي يعمل كلما تغيرت بعض القيم التابعة. يمكن القيام بذلك عن طريق تمرير هذه القيم في قائمة التبعيات.

على سبيل المثال ، يمكننا تشغيل useEffect متى تغير count على النحو التالي.

 import { useState, useEffect } from "react"; const App = () => { const [count, setCount] = useState(0); useEffect(() => { console.log(count); }, [count]); return ( <div> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default App;

سيتم تشغيل useEffect أعلاه عند استيفاء أي من هذين الشرطين.

  1. عند التركيب - بعد تقديم المكون.
  2. عندما تتغير قيمة count .

عند التحميل ، سيتم تشغيل تعبير console.log وسجّل count إلى 0. وبمجرد تحديث count ، يتم استيفاء الشرط الثاني ، وبالتالي يتم تشغيل useEffect مرة أخرى ، وسيستمر هذا عند النقر على الزر.

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

تنظيف التأثير

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

 useEffect(() => { console.log('mounted') return () => console.log('unmounting... clean up here') })

سيتم تسجيل خطاف useEffect أعلاه عند mounted المكون. إلغاء التركيب ... سيتم تسجيل التنظيف هنا عند فك المكون. يمكن أن يحدث هذا عند إزالة المكون من واجهة المستخدم.

عادة ما تتبع عملية التنظيف النموذج أدناه.

 useEffect(() => { //The effect we intend to make effect //We then return the clean up return () => the cleanup/unsubscription })

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

جلب البيانات وإعادة إحضارها باستخدام useEffect

واحدة من أكثر حالات استخدام الخطاف useEffect هي جلب البيانات وجلبها مسبقًا من واجهة برمجة التطبيقات.

لتوضيح ذلك ، سنستخدم بيانات المستخدم المزيفة التي أنشأتها من JSONPlaceholder لجلب البيانات باستخدام الخطاف useEffect .

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [users, setUsers] = useState([]); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/users"; useEffect(() => { const fetchUsers = async () => { const { data } = await axios.get(endPoint); setUsers(data); }; fetchUsers(); }, []); return ( <div className="App"> {users.map((user) => ( <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> ))} </div> ); }

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

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

يمكننا أيضًا إعادة إحضار البيانات عندما تتغير بعض الشروط. سنعرض هذا في الكود أدناه.

 import { useEffect, useState } from "react"; import axios from "axios"; export default function App() { const [userIDs, setUserIDs] = useState([]); const [user, setUser] = useState({}); const [currentID, setCurrentID] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/userdata/users"; useEffect(() => { axios.get(endPoint).then(({ data }) => setUserIDs(data)); }, []); useEffect(() => { const fetchUserIDs = async () => { const { data } = await axios.get(`${endPoint}/${currentID}`}); setUser(data); }; fetchUserIDs(); }, [currentID]); const moveToNextUser = () => { setCurrentID((prevId) => (prevId < userIDs.length ? prevId + 1 : prevId)); }; const moveToPrevUser = () => { setCurrentID((prevId) => (prevId === 1 ? prevId : prevId - 1)); }; return ( <div className="App"> <div> <h2>{user.name}</h2> <p>Occupation: {user.job}</p> <p>Sex: {user.sex}</p> </div> <button onClick={moveToPrevUser}>Prev</button> <button onClick={moveToNextUser}>Next</button> </div> ); }

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

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

بعد ذلك ، أنشأنا وظائف لتحديث قيمة id عند النقر فوق الأزرار. بمجرد تغيير قيمة id ، سيعمل useEffect مرة أخرى ويعيد جلب البيانات.

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

 useEffect(() => { const source = axios.CancelToken.source(); const fetchUsers = async () => { const { data } = await axios.get(`${endPoint}/${num}`, { cancelToken: source.token }); setUser(data); }; fetchUsers(); return () => source.cancel(); }, [num]);

هنا ، مررنا رمز Axios كمعامل ثانٍ إلى axios.get . عند فك المكون ، قمنا بإلغاء الاشتراك عن طريق استدعاء طريقة الإلغاء للكائن المصدر.

استخدام الخطاف useReducer

الخطاف useReducer هو عبارة عن خطاف React مفيد جدًا يقوم بعمل مماثل لخطاف useState . وفقًا لوثائق React ، يجب استخدام هذا الخطاف للتعامل مع منطق أكثر تعقيدًا من الخطاف useState . تجدر الإشارة إلى أن خطاف useState يتم تنفيذه داخليًا باستخدام الخطاف useReducer.

يأخذ الخطاف مخفضًا كوسيطة ويمكنه اختيار الحالة الأولية ووظيفة init كوسائط.

 const [state, dispatch] = useReducer(reducer, initialState, init)

هنا ، init هي وظيفة ويتم استخدامها كلما أردنا إنشاء الحالة الأولية كسول.

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

مثال تودو

أولاً ، يجب أن نصنع المخفض ليحمل الحالات.

 export const ADD_TODO = "ADD_TODO"; export const REMOVE_TODO = "REMOVE_TODO"; export const COMPLETE_TODO = "COMPLETE_TODO"; const reducer = (state, action) => { switch (action.type) { case ADD_TODO: const newTodo = { id: action.id, text: action.text, completed: false }; return [...state, newTodo]; case REMOVE_TODO: return state.filter((todo) => todo.id !== action.id); case COMPLETE_TODO: const completeTodo = state.map((todo) => { if (todo.id === action.id) { return { ...todo, completed: !todo.completed }; } else { return todo; } }); return completeTodo; default: return state; } }; export default reducer;

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

ثم أنشأنا وظيفة المخفض. كما هو الحال في Redux ، يجب أن يأخذ المخفض الحالة وكائن الإجراء. لكن بخلاف Redux ، لا نحتاج إلى تهيئة مخفضنا هنا.

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

ثم استخدمنا عبارات switch للتحقق من نوع الإجراء الذي تم تمريره من قبل المستخدم. إذا كان نوع الإجراء هو ADD_TODO ، فنحن نريد تمرير مهمة جديدة وإذا كانت REMOVE_TODO ، فنحن نريد تصفية المهام وإزالة تلك التي تتوافق مع id الذي تم تمريره من قبل المستخدم. إذا كانت COMPLETE_TODO ، فنحن نرغب في تعيين المهام وتبديل تلك التي تحتوي على id الذي تم تمريره من قبل المستخدم.

هذا هو ملف App.js حيث قمنا بتطبيق reducer .

 import { useReducer, useState } from "react"; import "./styles.css"; import reducer, { ADD_TODO, REMOVE_TODO, COMPLETE_TODO } from "./reducer"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = [ { id: id, text: "First Item", completed: false } ]; //We could also pass an empty array as the initial state //const initialState = [] const [state, dispatch] = useReducer(reducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); dispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="App"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit">+</button> </form> <div className="todos"> {state.map((todo) => ( <div key={todo.id} className="todoItem"> <p className={todo.completed && "strikethrough"}>{todo.text}</p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ))} </div> </div> ); }

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

ومع ذلك ، فإن السحر يحدث لأننا نستخدم خطاف useReducer . يقبل هذا الخطاف المخفض والحالة الأولية ويعيد الحالة ووظيفة الإرسال. هنا ، تخدم وظيفة الإرسال نفس الغرض مثل وظيفة الضبط لخطاف useState بأي شيء نريده بدلاً من dispatch .

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

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

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

على سبيل المثال ، يمكننا إنشاء دالة init على النحو التالي:

 const initFunc = () => [ { id: id, text: "First Item", completed: false } ]

ثم قم بتمريره إلى خطاف useReducer بنا.

 const [state, dispatch] = useReducer(reducer, initialState, initFunc)

إذا قمنا بذلك ، فإن initFunc سوف تتجاوز الحالة initialState التي قدمناها وسيتم حساب الحالة الأولية بشكل كسول.

خطاف useContext

توفر واجهة برمجة تطبيقات سياق React طريقة لمشاركة الحالات أو البيانات عبر شجرة مكونات React. كانت API متاحة في React ، كميزة تجريبية ، لفترة من الوقت لكنها أصبحت آمنة للاستخدام في React 16.3.0. تجعل واجهة برمجة التطبيقات مشاركة البيانات بين المكونات أمرًا سهلاً مع التخلص من الحفر بالدعامة.

بينما يمكنك تطبيق سياق React على التطبيق بأكمله ، فمن الممكن أيضًا تطبيقه على جزء من التطبيق.

لاستخدام الخطاف ، تحتاج أولاً إلى إنشاء سياق باستخدام React.createContext ويمكن بعد ذلك تمرير هذا السياق إلى الخطاف.

لتوضيح استخدام خطاف useContext ، دعنا ننشئ تطبيقًا بسيطًا من شأنه زيادة حجم الخط في جميع أنحاء تطبيقنا.

لنقم بإنشاء سياقنا في ملف context.js .

 import { createContext } from "react"; //Here, we set the initial fontSize as 16. const fontSizeContext = createContext(16); export default fontSizeContext;

هنا ، أنشأنا سياقًا وقمنا بتمرير قيمة أولية له تبلغ 16 ، ثم قمنا بتصدير السياق. بعد ذلك ، دعنا نربط سياقنا بتطبيقنا.

 import FontSizeContext from "./context"; import { useState } from "react"; import PageOne from "./PageOne"; import PageTwo from "./PageTwo"; const App = () => { const [size, setSize] = useState(16); return ( <FontSizeContext.Provider value={size}> <PageOne /> <PageTwo /> <button onClick={() => setSize(size + 5)}>Increase font</button> <button onClick={() => setSize((prevSize) => Math.min(11, prevSize - 5)) } > Decrease font </button> </FontSizeContext.Provider> ); }; export default App;

في الكود أعلاه ، قمنا بلف شجرة المكونات بالكامل باستخدام FontSizeContext.Provider وقمنا بتمرير size إلى عنصر القيمة الخاص به. هنا ، size هو حالة تم إنشاؤها باستخدام خطاف useState . هذا يسمح لنا بتغيير خاصية القيمة كلما تغيرت حالة size . من خلال تغليف المكون بالكامل Provider ، يمكننا الوصول إلى السياق في أي مكان في تطبيقنا.

على سبيل المثال ، وصلنا إلى السياق في <PageOne /> و <PageTwo /> . نتيجة لذلك ، سيزداد حجم الخط عبر هذين المكونين عندما نزيده من ملف App.js يمكننا زيادة أو تقليل حجم الخط من الأزرار كما هو موضح أعلاه وبمجرد أن نفعل ذلك ، يتغير حجم الخط في جميع أنحاء التطبيق.

 import { useContext } from "react"; import context from "./context"; const PageOne = () => { const size = useContext(context); return <p style={{ fontSize: `${size}px` }}>Content from the first page</p>; }; export default PageOne;

هنا ، وصلنا إلى السياق باستخدام الخطاف useContext من مكون PageOne بنا. ثم استخدمنا هذا السياق لتعيين خاصية حجم الخط. يتم تطبيق إجراء مماثل على ملف PageTwo.js .

تُعد السمات أو التكوينات الأخرى عالية المستوى على مستوى التطبيق مرشحين جيدين للسياقات.

استخدام useContext And useReducer

عند استخدامه مع الخطاف useReducer ، يسمح لنا useContext بإنشاء نظام إدارة الحالة الخاص بنا. يمكننا إنشاء دول عالمية وإدارتها بسهولة في تطبيقنا.

دعنا نحسن تطبيق المهام الخاص بنا باستخدام سياق API.

كالعادة ، نحتاج إلى إنشاء todoContext في ملف todoContext.js .

 import { createContext } from "react"; const initialState = []; export default createContext(initialState);

هنا أنشأنا السياق ، مررنا قيمة أولية لمصفوفة فارغة. ثم قمنا بتصدير السياق.

دعنا نعيد تشكيل ملف App.js الخاص بنا عن طريق فصل قائمة المهام والعناصر.

 import { useReducer, useState } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import TodoList from "./TodoList"; export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <div className="app"> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </TodoContext.Provider> ); }

هنا ، قمنا بلف ملف App.js بنا مع TodoContext.Provider ثم قمنا بتمرير قيم الإرجاع الخاصة بنا إلى todoReducer . هذا يجعل حالة المخفض ووظيفة dispatch يمكن الوصول إليها من خلال تطبيقنا.

ثم فصلنا عرض المهام إلى قائمة TodoList المكونة. لقد فعلنا ذلك بدون حفر دعامة ، بفضل واجهة برمجة التطبيقات (API) الخاصة بالسياق. دعنا نلقي نظرة على ملف TodoList.js .

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import Todo from "./Todo"; const TodoList = () => { const [state] = useContext(TodoContext); return ( <div className="todos"> {state.map((todo) => ( <Todo key={todo.id} todo={todo} /> ))} </div> ); }; export default TodoList;

باستخدام إتلاف المصفوفة ، يمكننا الوصول إلى الحالة (ترك وظيفة الإرسال) من السياق باستخدام الخطاف useContext . يمكننا بعد ذلك رسم خريطة من خلال الحالة وعرض عناصر المهام. ما زلنا نستخرج هذا في مكون Todo . تتطلب منا وظيفة الخريطة ES6 + تمرير مفتاح فريد ونظرًا لأننا نحتاج إلى مهمة محددة ، فإننا نمررها جنبًا إلى جنب أيضًا.

دعنا نلقي نظرة على مكون Todo .

 import React, { useContext } from "react"; import TodoContext from "./todoContext"; import { REMOVE_TODO, COMPLETE_TODO } from "./todoReducer"; const Todo = ({ todo }) => { const [, dispatch] = useContext(TodoContext); const removeTodo = (id) => { dispatch({ type: REMOVE_TODO, id }); }; const completeTodo = (id) => { dispatch({ type: COMPLETE_TODO, id }); }; return ( <div className="todoItem"> <p className={todo.completed ? "strikethrough" : "nostrikes"}> {todo.text} </p> <span onClick={() => removeTodo(todo.id)}>✕</span> <span onClick={() => completeTodo(todo.id)}>✓</span> </div> ); }; export default Todo;

مرة أخرى باستخدام إتلاف المصفوفة ، وصلنا إلى وظيفة الإرسال من السياق. هذا يسمح لنا بتعريف وظيفة completeTodo و removeTodo كما تمت مناقشته بالفعل في قسم useReducer . باستخدام خاصية todo التي تم تمريرها من todoList.js يمكننا عرض عنصر مهام. يمكننا أيضًا وضع علامة عليها على أنها مكتملة وإزالة المهمة كما نراها مناسبة.

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

لتوضيح ذلك ، دعنا نضيفها إلى مثال المهام.

هذا ما سنبنيه.

مرة أخرى ، علينا إنشاء themeContext . للقيام بذلك ، قم بإنشاء ملف themeContext.js وأضف الرموز التالية.

 import { createContext } from "react"; import colors from "./colors"; export default createContext(colors.light);

هنا ، قمنا بإنشاء سياق ومررنا colors.light كقيمة أولية. دعنا نحدد الألوان بهذه الخاصية في ملف colors.js .

 const colors = { light: { backgroundColor: "#fff", color: "#000" }, dark: { backgroundColor: "#000", color: "#fff" } }; export default colors;

في الكود أعلاه ، أنشأنا كائن colors يحتوي على خصائص فاتحة وداكنة. كل خاصية لها لون backgroundColor وكائن color .

بعد ذلك ، نقوم بإنشاء themeReducer للتعامل مع حالات السمة.

 import Colors from "./colors"; export const LIGHT = "LIGHT"; export const DARK = "DARK"; const themeReducer = (state, action) => { switch (action.type) { case LIGHT: return { ...Colors.light }; case DARK: return { ...Colors.dark }; default: return state; } }; export default themeReducer;

مثل جميع المخفضات ، يأخذ themeReducer الحالة والعمل. ثم يستخدم بيان switch لتحديد الإجراء الحالي. إذا كان من النوع LIGHT ، فإننا ببساطة نخصص دعائم Colors.light وإذا كانت من النوع DARK ، فإننا نعرض Colors.dark . كان بإمكاننا فعل ذلك بسهولة باستخدام خطاف useState لكننا اخترنا useReducer لتوجيه النقطة إلى المنزل.

بعد إعداد themeReducer ، يمكننا بعد ذلك دمجها في ملف App.js بنا.

 import { useReducer, useState, useCallback } from "react"; import "./styles.css"; import todoReducer, { ADD_TODO } from "./todoReducer"; import TodoContext from "./todoContext"; import ThemeContext from "./themeContext"; import TodoList from "./TodoList"; import themeReducer, { DARK, LIGHT } from "./themeReducer"; import Colors from "./colors"; import ThemeToggler from "./ThemeToggler"; const themeSetter = useCallback( theme => themeDispatch({type: theme}, [themeDispatch]); export default function App() { const [id, setId] = useState(0); const [text, setText] = useState(""); const initialState = []; const [todoState, todoDispatch] = useReducer(todoReducer, initialState); const [themeState, themeDispatch] = useReducer(themeReducer, Colors.light); const themeSetter = useCallback( (theme) => { themeDispatch({ type: theme }); }, [themeDispatch] ); const addTodoItem = (e) => { e.preventDefault(); const newId = id + 1; setId(newId); todoDispatch({ type: ADD_TODO, id: newId, text: text }); setText(""); }; return ( <TodoContext.Provider value={[todoState, todoDispatch]}> <ThemeContext.Provider value={[ themeState, themeSetter ]} > <div className="app" style={{ ...themeState }}> <ThemeToggler /> <h1>Todo Example</h1> <form className="input" onSubmit={addTodoItem}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button disabled={text.length === 0} type="submit"> + </button> </form> <TodoList /> </div> </ThemeContext.Provider> </TodoContext.Provider> ); }

في الكود أعلاه ، أضفنا بعض الأشياء إلى تطبيق المهام الموجود بالفعل. بدأنا باستيراد ThemeContext و themeReducer و ThemeToggler و Colors . We created a reducer using the useReducer hook, passing the themeReducer and an initial value of Colors.light to it. This returned the themeState and themeDispatch to us.

We then nested our component with the provider function from the ThemeContext , passing the themeState and the dispatch functions to it. We also added theme styles to it by spreading out the themeStates . This works because the colors object already defined properties similar to what the JSX styles will accept.

However, the actual theme toggling happens in the ThemeToggler component. دعونا نلقي نظرة عليه.

 import ThemeContext from "./themeContext"; import { useContext, useState } from "react"; import { DARK, LIGHT } from "./themeReducer"; const ThemeToggler = () => { const [showLight, setShowLight] = useState(true); const [themeState, themeSetter] = useContext(ThemeContext); const dispatchDarkTheme = () => themeSetter(DARK); const dispatchLightTheme = () => themeSetter(LIGHT); const toggleTheme = () => { showLight ? dispatchDarkTheme() : dispatchLightTheme(); setShowLight(!showLight); }; console.log(themeState); return ( <div> <button onClick={toggleTheme}> {showLight ? "Change to Dark Theme" : "Change to Light Theme"} </button> </div> ); }; export default ThemeToggler;

In this component, we used the useContext hook to retrieve the values we passed to the ThemeContext.Provider from our App.js file. As shown above, these values include the ThemeState , dispatch function for the light theme, and dispatch function for the dark theme. Thereafter, we simply called the dispatch functions to toggle the themes. We also created a state showLight to determine the current theme. This allows us to easily change the button text depending on the current theme.

The useMemo Hook

The useMemo hook is designed to memoize expensive computations. Memoization simply means caching. It caches the computation result with respect to the dependency values so that when the same values are passed, useMemo will just spit out the already computed value without recomputing it again. This can significantly improve performance when done correctly.

The hook can be used as follows:

 const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])

Let's consider three cases of the useMemo hook.

  1. When the dependency values, a and b remain the same.
    The useMemo hook will return the already computed memoized value without recomputation.
  2. When the dependency values, a and b change.
    The hook will recompute the value.
  3. When no dependency value is passed.
    The hook will recompute the value.

Let's take a look at an example to demonstrate this concept.

In the example below, we'll be computing the PAYE and Income after PAYE of a company's employees with fake data from JSONPlaceholder.

The calculation will be based on the personal income tax calculation procedure for Nigeria providers by PricewaterhouseCoopers available here.

This is shown in the sandbox below.

First, we queried the API to get the employees' data. We also get data for each employee (with respect to their employee id).

const [employee, setEmployee] = useState({}); const [employees, setEmployees] = useState([]); const [num, setNum] = useState(1); const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; useEffect(() => { const getEmployee = async () => { const { data } = await axios.get(`${endPoint}/${num}`); setEmployee(data); }; getEmployee(); }, [num]); useEffect(() => { axios.get(endPoint).then(({ data }) => setEmployees(data)); }, [num]);

استخدمنا axios وطريقة عدم async/await في أول useEffect ثم النقطة ثم بناء الجملة في الثانية. هذان النهجان يعملان بنفس الطريقة.

بعد ذلك ، باستخدام بيانات الموظف التي حصلنا عليها من الأعلى ، دعنا نحسب متغيرات التخفيف:

 const taxVariablesCompute = useMemo(() => { const { income, noOfChildren, noOfDependentRelatives } = employee; //supposedly complex calculation //tax relief computations for relief Allowance, children relief, // relatives relief and pension relief const reliefs = reliefAllowance1 + reliefAllowance2 + childrenRelief + relativesRelief + pensionRelief; return reliefs; }, [employee]);

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

علاوة على ذلك ، باستخدام قيم الإعفاء الضريبي التي تم الحصول عليها أعلاه ، نود حساب PAYE والدخل بعد PAYE.

 const taxCalculation = useMemo(() => { const { income } = employee; let taxableIncome = income - taxVariablesCompute; let PAYE = 0; //supposedly complex calculation //computation to compute the PAYE based on the taxable income and tax endpoints const netIncome = income - PAYE; return { PAYE, netIncome }; }, [employee, taxVariablesCompute]);

أجرينا حساب الضريبة (عملية حسابية معقدة إلى حد ما) باستخدام متغيرات الضرائب المحسوبة أعلاه ثم قمنا بتدوينها باستخدام الخطاف useMemo .

الكود الكامل متاح هنا.

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

بعد حساب القيم ، عرضنا النتيجة ببساطة.

لاحظ ما يلي حول خطاف useMemo .

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

خطاف useCallback

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

على سبيل المثال ، ضع في اعتبارك الرموز التالية أدناه.

 import React, {useCallback, useMemo} from 'react' const MemoizationExample = () => { const a = 5 const b = 7 const memoResult = useMemo(() => a + b, [a, b]) const callbackResult = useCallback(a + b, [a, b]) console.log(memoResult) console.log(callbackResult) return( <div> ... </div> ) } export default MemoizationExample

في المثال أعلاه ، سيعطي كل من memoResult و callbackResult نفس القيمة 12 . هنا ، useCallback القيمة المحفوظة في الذاكرة. ومع ذلك ، يمكننا أيضًا أن نجعله يعيد رد نداء مذكّر بتمريره كدالة.

سيؤدي استخدام useCallback أدناه إلى إعادة اتصال مذكّر.

 ... const callbackResult = useCallback(() => a + b, [a, b]) ...

يمكننا بعد ذلك تشغيل رد الاتصال عند تنفيذ إجراء أو في خطاف useEffect .

 import {useCallback, useEffect} from 'react' const memoizationExample = () => { const a = 5 const b = 7 const callbackResult = useCallback(() => a + b, [a, b]) useEffect(() => { const callback = callbackResult() console.log(callback) }) return ( <div> <button onClick= {() => console.log(callbackResult())}> Trigger Callback </button> </div> ) } export default memoizationExample

في الكود أعلاه ، قمنا بتعريف وظيفة رد الاتصال باستخدام الخطاف useCallback . ثم قمنا باستدعاء رد الاتصال في خطاف useEffect عند تثبيت المكون وأيضًا عند النقر فوق الزر.

ينتج عن كل من useEffect والنقر فوق الزر نفس النتيجة.

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

استخدام useRef هوك

يُرجع useRef كائنًا يمكن أن يستمر في أحد التطبيقات. يحتوي الخطاف على خاصية واحدة فقط ، current ، ويمكننا بسهولة تمرير وسيطة إليه.

إنه يخدم نفس الغرض وهو createRef المستخدم في المكونات المستندة إلى الفصل. يمكننا إنشاء مرجع باستخدام هذا الخطاف على النحو التالي:

 const newRef = useRef('')

هنا أنشأنا مرجعًا جديدًا يسمى newRef بتمرير سلسلة فارغة إليه.

يستخدم هذا الخطاف بشكل أساسي لغرضين:

  1. الوصول إلى DOM أو التلاعب به ، و
  2. تخزين الحالات القابلة للتغيير - يكون هذا مفيدًا عندما لا نريد عرض المكون عندما تتغير القيمة.

التلاعب في DOM

عند تمريره إلى عنصر DOM ، يشير الكائن ref إلى هذا العنصر ويمكن استخدامه للوصول إلى سمات وخصائص DOM الخاصة به.

هنا مثال بسيط للغاية لتوضيح هذا المفهوم.

 import React, {useRef, useEffect} from 'react' const RefExample = () => { const headingRef = useRef('') console.log(headingRef) return( <div> <h1 className='topheading' ref={headingRef}>This is a h1 element</h1> </div> ) } export default RefExample

في المثال أعلاه ، قمنا بتعريف العنوان headingRef باستخدام الخطاف useRef الذي يمرر سلسلة فارغة. نقوم بعد ذلك بتعيين المرجع في علامة h1 بتمرير ref = {headingRef} . بتعيين هذا المرجع ، طلبنا من headingRef أن يشير إلى عنصر h1 الخاص بنا. هذا يعني أنه يمكننا الوصول إلى خصائص عنصر h1 من المرجع.

لرؤية هذا ، إذا تحققنا من قيمة console.log console.log(headingRef) ، فسنحصل على {current: HTMLHeadingElement} أو {current: h1} ويمكننا تقييم جميع خصائص أو سمات العنصر. شيء مماثل ينطبق على أي عنصر HTML آخر.

على سبيل المثال ، يمكننا جعل النص مائلًا عند تركيب المكون.

 useEffect(() => { headingRef.current.style.font; }, []);

يمكننا حتى تغيير النص إلى شيء آخر.

 ... headingRef.current.innerHTML = "A Changed H1 Element"; ...

يمكننا أيضًا تغيير لون الخلفية للحاوية الأصلية.

 ... headingRef.current.parentNode.style.backgroundColor = "red"; ...

يمكن إجراء أي نوع من التلاعب في DOM هنا. لاحظ أنه يمكن قراءة العنوان الحالي بنفس طريقة قراءة document.querySelector('.topheading') headingRef.current

إحدى حالات الاستخدام المثيرة لخطاف useRef في معالجة عنصر DOM هي تركيز المؤشر على عنصر الإدخال. دعونا نجري بسرعة من خلاله.

 import {useRef, useEffect} from 'react' const inputRefExample = () => { const inputRef = useRef(null) useEffect(() => { inputRef.current.focus() }, []) return( <div> <input ref={inputRef} /> <button onClick = {() => inputRef.current.focus()}>Focus on Input </button> </div> ) } export default inputRefExample

في الكود أعلاه ، أنشأنا inputRef باستخدام الخطاف useRef ثم طلبناه للإشارة إلى عنصر الإدخال. ثم جعلنا المؤشر يركز على مرجع الإدخال عند تحميل المكون وعند النقر فوق الزر باستخدام inputRef.current.focus() . هذا ممكن لأن focus() هو سمة من سمات عناصر الإدخال وبالتالي فإن المرجع سيكون قادرًا على تقييم الطرق.

يمكن تقييم المراجع التي تم إنشاؤها في مكون رئيسي في المكون الفرعي عن طريق إعادة توجيهها باستخدام React.forwardRef() . دعونا نلقي نظرة عليه.

لنقم أولاً بإنشاء مكون آخر NewInput.js ونضيف إليه الأكواد التالية.

 import { useRef, forwardRef } from "react"; const NewInput = forwardRef((props, ref) => { return <input placeholder={props.val} ref={ref} />; }); export default NewInput;

يقبل هذا المكون props ref . لقد مررنا المرجع إلى ref و props.val الخاص به لعنصره النائب. لا تأخذ مكونات React العادية سمة ref . هذه السمة متاحة فقط عندما نلفها بـ React.forwardRef كما هو موضح أعلاه.

يمكننا بعد ذلك استدعاء هذا بسهولة في المكون الرئيسي.

 ... <NewInput val="Just an example" ref={inputRef} /> ...

تخزين الدول المتغيرة

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

سيكتشف المثال التالي عدد المرات التي يتم فيها النقر فوق الزر دون إعادة عرض المكون.

 import { useRef } from "react"; export default function App() { const countRef = useRef(0); const increment = () => { countRef.current++; console.log(countRef); }; return ( <div className="App"> <button onClick={increment}>Increment </button> </div> ); }

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

لاحظ أنه بينما يكون useState غير متزامن ، يكون useRef متزامنًا. بمعنى آخر ، تكون القيمة متاحة فور تحديثها.

خطاف useLayoutEffect

مثل الخطاف useEffect ، useLayoutEffect بعد تثبيت المكون وعرضه. يتم إطلاق هذا الخطاف بعد طفرة DOM ويتم ذلك بشكل متزامن. بصرف النظر عن الاستدعاء المتزامن بعد طفرة DOM ، فإن useLayoutEffect تفعل نفس الشيء مثل useEffect .

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

دعنا نلقي نظرة على بعض الأمثلة لتوضيح هذا المفهوم.

  1. سنحصل على عرض النافذة وارتفاعها عند تغيير الحجم.
 import {useState, useLayoutEffect} from 'react' const ResizeExample = () =>{ const [windowSize, setWindowSize] = useState({width: 0, height: 0}) useLayoutEffect(() => { const resizeWindow = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }) window.addEventListener('resize', resizeWindow) return () => window.removeEventListener('resize', resizeWindow) }, []) return ( <div> <p>width: {windowSize.width}</p> <p>height: {windowSize.height}</p> </div> ) } export default ResizeExample

في الكود أعلاه ، أنشأنا windowSize العرض والارتفاع. ثم نقوم بتعيين الحالة على عرض النافذة الحالية وارتفاعها على التوالي عند تغيير حجم النافذة. قمنا أيضًا بتنظيف الكود عند فكه. عملية التنظيف ضرورية في useLayoutEffect لتنظيف معالجة DOM وتحسين الكفاءة.

  1. لنقم بتمويه النص باستخدام useLayoutEffect .
 import { useRef, useState, useLayoutEffect } from "react"; export default function App() { const paragraphRef = useRef(""); useLayoutEffect(() => { const { current } = paragraphRef; const blurredEffect = () => { current.style.color = "transparent"; current.style.textShadow = "0 0 5px rgba(0,0,0,0.5)"; }; current.addEventListener("click", blurredEffect); return () => current.removeEventListener("click", blurredEffect); }, []); return ( <div className="App"> <p ref={paragraphRef}>This is the text to blur</p> </div> ); }

استخدمنا useRef و useLayoutEffect معًا في الكود أعلاه. أنشأنا أولاً مرجع paragraphRef مرجعي للإشارة إلى فقرتنا. ثم أنشأنا مستمع حدث عند النقر لمراقبة وقت النقر على الفقرة ثم طمسها باستخدام خصائص النمط التي حددناها. أخيرًا ، قمنا بتنظيف مستمع الحدث باستخدام removeEventListener .

useDispatch and useSelector

useDispatch هو خطاف إعادة الإرسال لإرسال (تشغيل) الإجراءات في أحد التطبيقات. يأخذ كائن الإجراء كحجة ويستدعي الإجراء. useDispatch هو تعادل الخطاف لـ mapDispatchToProps .

من ناحية أخرى ، فإن useSelector عبارة عن خطاف Redux لتقييم حالات Redux. يتطلب الأمر وظيفة لتحديد مخفض Redux الدقيق من المتجر ثم إرجاع الحالات المقابلة.

بمجرد توصيل متجر Redux الخاص بنا بتطبيق React من خلال موفر Redux ، يمكننا استدعاء الإجراءات باستخدام useDispatch والوصول إلى الحالات باستخدام useSelector . يمكن تقييم كل إجراء وحالة Redux باستخدام هذين الخطافين.

لاحظ أن هذه الحالات تأتي مع React Redux (حزمة تجعل تقييم متجر Redux أمرًا سهلاً في تطبيق React). لم تكن متوفرة في مكتبة Redux الأساسية.

هذه الخطافات سهلة الاستخدام للغاية. أولاً ، علينا الإعلان عن وظيفة الإرسال ثم تشغيلها.

 import {useDispatch, useSelector} from 'react-redux' import {useEffect} from 'react' const myaction from '...' const ReduxHooksExample = () =>{ const dispatch = useDispatch() useEffect(() => { dispatch(myaction()); //alternatively, we can do this dispatch({type: 'MY_ACTION_TYPE'}) }, []) const mystate = useSelector(state => state.myReducerstate) return( ... ) } export default ReduxHooksExample

في الكود أعلاه ، قمنا باستيراد useDispatch و useSelector من react-redux . ثم ، في خطاف useEffect ، أرسلنا الإجراء. يمكننا تحديد الإجراء في ملف آخر ثم تسميته هنا أو يمكننا تحديده مباشرة كما هو موضح في استدعاء useEffect .

بمجرد إرسال الإجراءات ، ستكون دولنا متاحة. يمكننا بعد ذلك استرداد الحالة باستخدام الخطاف useSelector كما هو موضح. يمكن استخدام الحالات بنفس الطريقة التي نستخدم بها الحالات من خطاف useState .

دعنا نلقي نظرة على مثال لتوضيح هذين الخطافين.

لإثبات هذا المفهوم ، يتعين علينا إنشاء متجر Redux ومخفض وإجراءات. لتبسيط الأمور هنا ، سنستخدم مكتبة Redux Toolkit مع قاعدة البيانات المزيفة الخاصة بنا من JSONPlaceholder.

نحتاج إلى تثبيت الحزم التالية للبدء. قم بتشغيل أوامر bash التالية.

 npm i redux @reduxjs/toolkit react-redux axios

أولاً ، لنقم بإنشاء employeesSlice.js للتعامل مع المخفض والعمل لواجهة برمجة التطبيقات الخاصة بموظفينا.

 import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import axios from "axios"; const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees"; export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => { const { data } = await axios.get(endPoint); return data; }); const employeesSlice = createSlice({ name: "employees", initialState: { employees: [], loading: false, error: "" }, reducers: {}, extraReducers: { [fetchEmployees.pending]: (state, action) => { state.status = "loading"; }, [fetchEmployees.fulfilled]: (state, action) => { state.status = "success"; state.employees = action.payload; }, [fetchEmployees.rejected]: (state, action) => { state.status = "error"; state.error = action.error.message; } } }); export default employeesSlice.reducer;

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

تعمل مجموعة أدوات Redux أيضًا على تسهيل إعداد المتجر. هنا المتجر.

 import { configureStore } from "@reduxjs/toolkit"; import { combineReducers } from "redux"; import employeesReducer from "./employeesSlice"; const reducer = combineReducers({ employees: employeesReducer }); export default configureStore({ reducer });;

هنا ، استخدمنا الدمج بين configureStore التخفيض لتجميع المخفضات combineReducers التي توفرها مجموعة أدوات Redux لإعداد المتجر.

دعنا ننتقل إلى استخدام هذا في تطبيقنا.

أولاً ، نحتاج إلى توصيل Redux بتطبيق React الخاص بنا. من الناحية المثالية ، ينبغي أن يتم ذلك في جذر طلبنا. أحب أن أفعل ذلك في ملف index.js .

 import React, { StrictMode } from "react"; import ReactDOM from "react-dom"; import store from "./redux/store"; import { Provider } from "react-redux"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <StrictMode> <App /> </StrictMode> </Provider>, rootElement );

هنا ، قمت باستيراد المتجر الذي قمت بإنشائه أعلاه وأيضًا Provider من react-redux .

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

يمكننا بعد ذلك متابعة استخدام useDispatch و useSelector لجلب البيانات.

لنفعل ذلك في ملف App.js بنا.

 import { useDispatch, useSelector } from "react-redux"; import { fetchEmployees } from "./redux/employeesSlice"; import { useEffect } from "react"; export default function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(fetchEmployees()); }, [dispatch]); const employeesState = useSelector((state) => state.employees); const { employees, loading, error } = employeesState; return ( <div className="App"> {loading ? ( "Loading..." ) : error ? ( <div>{error}</div> ) : ( <> <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <h3>{`${employee.firstName} ${employee.lastName}`}</h3> </div> ))} </> )} </div> ); }

في الكود أعلاه ، استخدمنا الخطاف useDispatch لاستدعاء إجراء fetchEmployees الذي تم إنشاؤه في ملف employeesSlice.js . هذا يجعل حالة الموظفين متاحة في تطبيقنا. ثم استخدمنا الخطاف useSelector للحصول على الحالات. بعد ذلك ، عرضنا النتائج من خلال تعيين employees .

خطاف useHistory

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

لاستخدام أي خطاف لـ React Router ، يجب علينا أولاً تغليف تطبيقنا بـ BrowserRouter . يمكننا بعد ذلك تداخل المسارات مع Switch and Route .

لكن أولاً ، يتعين علينا تثبيت الحزمة عن طريق تشغيل الأوامر التالية.

 npm install react-router-dom

بعد ذلك ، نحتاج إلى إعداد تطبيقنا على النحو التالي. أحب القيام بذلك في ملف App.js بي.

 import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import Employees from "./components/Employees"; export default function App() { return ( <div className="App"> <Router> <Switch> <Route path='/'> <Employees /> </Route> ... </Switch> </Router> </div> ); }

يمكن أن يكون لدينا أكبر عدد ممكن من المسارات اعتمادًا على عدد المكونات التي نرغب في عرضها. هنا ، قدمنا ​​عنصر Employees فقط. تخبر سمة path React Router DOM بمسار المكون ويمكن تقييمها باستخدام سلسلة استعلام أو طرق أخرى مختلفة.

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

 <Route path='/' exact > <Employees /> </Route>

الآن بعد أن أعددنا جهاز التوجيه ، يمكننا بعد ذلك استخدام خطاف useHistory Router الأخرى في تطبيقنا.

لاستخدام الخطاف useHistory ، نحتاج أولاً إلى التصريح عنه على النحو التالي.

 import {useHistory} from 'history' import {useHistory} from 'react-router-dom' const Employees = () =>{ const history = useHistory() ... }

إذا قمنا بتسجيل المحفوظات في وحدة التحكم ، فسنرى العديد من الخصائص المرتبطة بها. وتشمل هذه block ، createHref ، go ، goBack ، goForward ، length ، listen ، location ، push ، replace . في حين أن كل هذه الخصائص مفيدة ، فمن المرجح أن تستخدم history.push و history.replace أكثر من الخصائص الأخرى.

دعنا نستخدم هذه الخاصية للانتقال من صفحة إلى أخرى.

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

 function moveToPage = (id) =>{ history.push(`/employees/${id}`) }

يمكننا تنفيذ ذلك في ملف Employee.js الخاص بنا عن طريق إضافة ما يلي.

 import { useEffect } from "react"; import { Link, useHistory, useLocation } from "react-router-dom"; export default function Employees() { const history = useHistory(); function pushToPage = (id) => { history.push(`/employees/${id}`) } ... return ( <div> ... <h1>List of Employees</h1> {employees.map((employee) => ( <div key={employee.id}> <span>{`${employee.firstName} ${employee.lastName} `}</span> <button onClick={pushToPage(employee.id)}> » </button> </div> ))} </div> ); }

في وظيفة pushToPage ، استخدمنا history من خطاف useHistory للانتقال إلى صفحة الموظف وتمرير معرف الموظف جنبًا إلى جنب.

خطاف useLocation

يأتي هذا الخطاف أيضًا مع React Router DOM. إنه خطاف شائع جدًا يستخدم للعمل مع معلمة سلسلة الاستعلام. يشبه هذا الخطاف window.location في المتصفح.

 import {useLocation} from 'react' const LocationExample = () =>{ const location = useLocation() return ( ... ) } export default LocationExample

يقوم الخطاف useLocation بإرجاع pathname ومعلمة search hash state . تتضمن المعلمات الأكثر استخدامًا pathname search ، ولكن يمكنك استخدام hash بشكل متساوٍ ، state الكثير في تطبيقك.

ستعيد خاصية pathname الموقع المسار الذي حددناه في إعداد Route الخاص بنا. بينما سيعود search إلى معامل بحث الاستعلام إن وجد. على سبيل المثال ، إذا مررنا 'http://mywebsite.com/employee/?id=1' إلى pathname ، فسيكون اسم المسار /employee وسيكون search ?id=1 .

يمكننا بعد ذلك استرداد معلمات البحث المختلفة باستخدام حزم مثل سلسلة الاستعلام أو عن طريق ترميزها.

خطاف useParams

إذا قمنا بإعداد المسار الخاص بنا باستخدام معلمة URL في سمة المسار الخاصة به ، فيمكننا تقييم هذه المعلمات كأزواج مفتاح / قيمة مع الخطاف useParams .

على سبيل المثال ، لنفترض أن لدينا المسار التالي.

 <Route path='/employees/:id' > <Employees /> </Route>

سيتوقع المسار معرفًا ديناميكيًا بدلاً من :id .

باستخدام الخطاف useParams ، يمكننا تقييم المعرف الذي تم تمريره بواسطة المستخدم ، إن وجد.

على سبيل المثال ، بافتراض أن المستخدم يمرر ما يلي في الوظيفة مع history.push ،

 function goToPage = () => { history.push(`/employee/3`) }

يمكننا استخدام الخطاف useParams للوصول إلى معلمة URL هذه على النحو التالي.

 import {useParams} from 'react-router-dom' const ParamsExample = () =>{ const params = useParams() console.log(params) return( <div> ... </div> ) } export default ParamsExample

إذا قمنا بتسجيل params إلى وحدة التحكم ، فسنحصل على الكائن التالي {id: "3"} .

خطاف useRouteMatch

يوفر هذا الخطاف الوصول إلى كائن المطابقة. تقوم بإرجاع أقرب تطابق للمكون إذا لم يتم توفير وسيطة له.

يُرجع كائن المطابقة العديد من المعلمات بما في ذلك path (نفس المسار المحدد في المسار) URL وكائن isExact params

على سبيل المثال ، يمكننا استخدام useRouteMatch لإرجاع المكونات بناءً على المسار.

 import { useRouteMatch } from "react-router-dom"; import Employees from "..."; import Admin from "..." const CustomRoute = () => { const match = useRouteMatch("/employees/:id"); return match ? ( <Employee /> ) : ( <Admin /> ); }; export default CustomRoute;

في الكود أعلاه ، قمنا بتعيين مسار المسار باستخدام useRouteMatch ثم عرضنا المكون <Employee /> أو <Admin /> اعتمادًا على المسار الذي حدده المستخدم.

لكي يعمل هذا ، ما زلنا بحاجة إلى إضافة المسار إلى ملف App.js بنا.

 ... <Route> <CustomRoute /> </Route> ...

بناء خطاف مخصص

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

تسمح لنا الخطافات المخصصة بكتابة الوظائف مرة واحدة وإعادة استخدامها كلما دعت الحاجة إليها ، وبالتالي الامتثال لمبدأ DRY.

على سبيل المثال ، يمكننا إنشاء خطاف مخصص للحصول على موضع التمرير على صفحتنا على النحو التالي.

 import { useLayoutEffect, useState } from "react"; export const useScrollPos = () => { const [scrollPos, setScrollPos] = useState({ x: 0, y: 0 }); useLayoutEffect(() => { const getScrollPos = () => setScrollPos({ x: window.pageXOffset, y: window.pageYOffset }); window.addEventListener("scroll", getScrollPos); return () => window.removeEventListener("scroll", getScrollPos); }, []); return scrollPos; };

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

يمكننا استخدام هذا الخطاف المخصص في أي مكان في تطبيقنا عن طريق الاتصال به واستخدامه تمامًا كما نستخدم أي دولة أخرى.

 import {useScrollPos} from './Scroll' const App = () =>{ const scrollPos = useScrollPos() console.log(scrollPos.x, scrollPos.y) return ( ... ) } export default App

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

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

خاتمة

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

أنا أشجعك على تجربة هذه الروابط في التطبيق الخاص بك لفهمها أكثر.

موارد من The React Docs

  • الأسئلة الشائعة حول الخطافات
  • مجموعة أدوات Redux
  • باستخدام State Hook
  • استخدام خطاف التأثير
  • مرجع API الخطاف
  • رد فعل خطاف الإعادة
  • تفاعل خطاف جهاز التوجيه