الفرق بين المراجعتين لصفحة: «React/hooks state»
جميل-بيلوني (نقاش | مساهمات) إنشاء الصفحة |
تحديث |
||
(مراجعتان متوسطتان بواسطة مستخدم واحد آخر غير معروضتين) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:خطاف الحالة في React}}</noinclude> | |||
''الخطافات هي إضافة جديدة إلى الإصدار 16.8 في React، إذ تسمح لك باستعمال ميزة الحالة وميزات React الأخرى دون كتابة أي صنف.'' | ''الخطافات هي إضافة جديدة إلى الإصدار 16.8 في React، إذ تسمح لك باستعمال ميزة الحالة وميزات React الأخرى دون كتابة أي صنف.'' | ||
سطر 20: | سطر 21: | ||
== مثال عن صنف مكافئ لخطاف == | == مثال عن صنف مكافئ لخطاف == | ||
إن استعملت الأصناف في React من قبل، فيجب أن تكون الشيفرة التالية | إن استعملت الأصناف في [[React]] من قبل، فيجب أن تكون الشيفرة التالية مألوفةً لديك:<syntaxhighlight lang="javascript"> | ||
class Example extends React.Component { | class Example extends React.Component { | ||
constructor(props) { | constructor(props) { | ||
سطر 40: | سطر 41: | ||
} | } | ||
} | } | ||
'''ملاحظة''': قد تتساءل عن سبب استعمالنا | </syntaxhighlight>الحالة الأولية تكون <code>{ count: 0 }</code>، ونعمل على زيادة <code>state.count</code> عندما يضغط المستخدم على زر يستدعي <code>this.setState()</code>. سنستعمل أجزاء من هذا الصنف في أقسام لاحقة من هذه الصفحة. | ||
'''ملاحظة''': قد تتساءل عن سبب استعمالنا مثال عدَّادٍ هنا عوضَ استعمال مثال أكثر واقعية. يكمن السبب في أنَّ هذا المثال البسيط يساعدنا على التركيز على الواجهة البرمجية في بداية رحلتنا مع الخطافات. | |||
== الخطافات ومكونات دالة == | == الخطافات ومكونات دالة == | ||
لنتذكر سويةً، تبدو مكونات دالة في React بالشكل:<syntaxhighlight lang="javascript"> | لنتذكر سويةً، تبدو مكونات دالة في [[React]] بالشكل:<syntaxhighlight lang="javascript"> | ||
const Example = (props) => { | const Example = (props) => { | ||
// تستطيع استعمال الخطافات هنا | // تستطيع استعمال الخطافات هنا | ||
سطر 55: | سطر 57: | ||
return <div />; | return <div />; | ||
} | } | ||
</syntaxhighlight>ربما كنت تعرف مسبقًا | </syntaxhighlight>ربما كنت تعرف مسبقًا أنَّ هذه المكونات هي "مكونات عديمة الحالة" (stateless components). نعرِّف الآن إمكانية استعمال حالة [[React]] من هذه المكونات، لذا نفضل استعمال الاسم "مكونات دالة". | ||
لا تعمل الخطافات داخل الأصناف، ولكن يمكن استعمالها بدلًا من | لا تعمل الخطافات داخل الأصناف، ولكن يمكن استعمالها بدلًا من الأصناف نفسها. | ||
== ما هو الخطاف؟ == | == ما هو الخطاف؟ == | ||
يبدأ مثالنا الجديد باستيراد الخطاف useState من React:<syntaxhighlight lang="javascript"> | يبدأ مثالنا الجديد باستيراد الخطاف <code>useState</code> من [[React]]:<syntaxhighlight lang="javascript"> | ||
import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||
سطر 66: | سطر 68: | ||
// ... | // ... | ||
} | } | ||
</syntaxhighlight>إذًا، ما هو الخطاف تحديدًا؟ الخطاف هو دالة مميزة تمكنك من "تعليق أو ربط" (hook into) ميزات React مع بعضها. على سبيل المثال، useState هو خطاف يمكنك من إضافة حالة React إلى مكونات دالة. سنتطرق إلى الخطافات الأخرى لاحقًا. | </syntaxhighlight>إذًا، ما هو الخطاف تحديدًا؟ الخطاف هو دالة مميزة تمكنك من "تعليق أو ربط" (hook into) ميزات [[React]] مع بعضها. على سبيل المثال، <code>useState</code> هو خطاف يمكنك من إضافة حالة [[React]] إلى مكونات دالة. سنتطرق إلى الخطافات الأخرى لاحقًا. | ||
متى يمكنني استعمال الخطاف؟ | '''متى يمكنني استعمال الخطاف؟''' | ||
ملاحظة: هنالك بعض القواعد | إن كتبت مكون دالة ووجدت أنَّك بحاجة إلى إضافة بعض الحالة له، فستحتاج - سابقًا قبل عصر الخطافات - إلى تحويله إلى صنف. الآن، يمكنك استعمال خطاف داخل مكون دالة موجودة وهو ما سنفعله الآن. | ||
'''ملاحظة''': هنالك بعض [[React/hooks rules|القواعد المخصَّصة]] تحدِّد المكان المسموح والمحظور فيه استعمال الخطافات ضمن مكون. سنشرح هذه القواعد بالتفصيل الممل في توثيق [[React/hooks rules|قواعد استعمال الخطافات]]. | |||
== التصريح عن متغير حالة == | == التصريح عن متغير حالة == | ||
إن كنا سنستعمل صنفًا، نهيِّئ فيه الحالة count إلى 0 عبر ضبط this.state إلى { count: 0 } في باني هذا الصنف:<syntaxhighlight lang="javascript"> | إن كنا سنستعمل صنفًا، نهيِّئ فيه الحالة <code>count</code> إلى 0 عبر ضبط <code>this.state</code> إلى <code>{ count: 0 }</code> في باني هذا الصنف:<syntaxhighlight lang="javascript"> | ||
class Example extends React.Component { | class Example extends React.Component { | ||
constructor(props) { | constructor(props) { | ||
سطر 81: | سطر 85: | ||
}; | }; | ||
} | } | ||
</syntaxhighlight>في مكون دالة، لا يمكننا استعمال | </syntaxhighlight>في مكون دالة، لا يمكننا استعمال <code>this</code>، لذا لا نستطيع إسناد أو قراء <code>this.state</code>. عوض ذلك، يمكننا استدعاء الخطاف <code>useState</code> مباشرةً داخل المكون الخاص بنا:<syntaxhighlight lang="javascript"> | ||
import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||
سطر 87: | سطر 91: | ||
// "count" التصريح عن متغير حالة جديد ندعوه | // "count" التصريح عن متغير حالة جديد ندعوه | ||
const [count, setCount] = useState(0); | const [count, setCount] = useState(0); | ||
</syntaxhighlight>ماذا يفعل استدعاء | </syntaxhighlight>'''ماذا يفعل استدعاء <code>useState</code>؟''' | ||
إنَّه يصرِّح عن "متغير حالة" (state variable). هذا المتغير يدعى <code>count</code> ولكن يمكننا أن ندعوه بأي اسم آخر مثل <code>banana</code>. هذه هي طريقة لحفظ بعض القيم بين استدعاءات الدالة، إذ <code>useState</code> هي طريقة جديدة لاستعمال الامكانيات نفسها التي توفرها <code>this.state</code> في الصنف. عمومًا، المتغيرات "تختفي" عند اكتمال تنفيذ الدالة وخروجها ولكن متغيرات الحالة تحافظ على قيمتها في [[React]]. | |||
لماذا نمرر وسيطًا إلى useState؟ | '''لماذا نمرر وسيطًا إلى useState؟''' | ||
الوسيط الوحيد الذي يمكن تمريره إلى الخطاف useState() هو الحالة الأولية. خلافًا للأصناف، ليس من الضروري أن تكون الحالة كائنًا. يمكننا استعمال عدد أو سلسلة نصية إن كان ذلك ما نحتاجه. في مثالنا، نحتاج إلى عدد ليمثل عدد المرات التي ضغط فيها المستخدم على الزر، لذا مرَّرنا العدد 0 ليكون الحالة الأولية لمتغيرنا. (إن أردنا تخزين قيمتين مختلفتين في حالة، فيمكننا فعل ذلك عبر استدعاء useState() مرتين.) | الوسيط الوحيد الذي يمكن تمريره إلى الخطاف <code>useState()</code> هو الحالة الأولية. خلافًا للأصناف، ليس من الضروري أن تكون الحالة كائنًا. يمكننا استعمال عدد أو سلسلة نصية إن كان ذلك ما نحتاجه. في مثالنا، نحتاج إلى عدد ليمثل عدد المرات التي ضغط فيها المستخدم على الزر، لذا مرَّرنا العدد 0 ليكون الحالة الأولية لمتغيرنا. (إن أردنا تخزين قيمتين مختلفتين في حالة، فيمكننا فعل ذلك عبر استدعاء <code>useState()</code> مرتين.) | ||
ما الذي يعيده | '''ما الذي يعيده <code>useState</code>؟''' | ||
إنه يعيد زوجًا من القيم: الحالة الحالية، ودالة تحدِّثها. لهذا السبب، نكتب const [count, setCount] = useState(). هذا الأمر يشبه this.state.count و this.setState في الصنف، باستثناء | إنه يعيد زوجًا من القيم: الحالة الحالية، ودالة تحدِّثها. لهذا السبب، نكتب <code>const [count, setCount] = useState()</code>. هذا الأمر يشبه <code>this.state.count</code> و <code>this.setState</code> في الصنف، باستثناء أنَّنا نحصل عليهم كزوج من القيم. إن لم تكن الصيغة السابقة التي استعملناها مألوفة لك، فسنتطرق إليها في [[React/hooks state#.D8.A5.D8.B6.D8.A7.D9.81.D8.A9: .D9.85.D8.A7 .D8.A7.D9.84.D8.B0.D9.8A .D8.AA.D8.B9.D9.86.D9.8A.D9.87 .D8.A7.D9.84.D8.A3.D9.82.D9.88.D8.A7.D8.B3 .D8.A7.D9.84.D9.85.D8.B9.D9.82.D9.88.D9.81.D8.A9.D8.9F|آخر هذه الصفحة]]. | ||
الآن وقد عرفنا ما الذي يفعله الخطاف | الآن وقد عرفنا ما الذي يفعله الخطاف <code>useState</code>، يجب أن تكون الشيفرة التالية مفهومة تمامًا لك:<syntaxhighlight lang="javascript"> | ||
import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||
سطر 103: | سطر 109: | ||
// "count" التصريح عن متغير حالة جديد ندعوه | // "count" التصريح عن متغير حالة جديد ندعوه | ||
const [count, setCount] = useState(0); | const [count, setCount] = useState(0); | ||
</syntaxhighlight>صرَّحنا عن متغير حالة يدعى | </syntaxhighlight>صرَّحنا عن متغير حالة يدعى <code>count</code>، وأسندنا القيمة 0 العددية له. ستتذكر [[React]] قيمته الحالية بين عمليات إعادة التصيير، وتوفر الحالة الأحدث له للدالة. إن أردنا تحديث قيمة المتغير <code>count</code> الحالية، يمكننا استدعاء <code>useState</code>. | ||
ملاحظة: قد تتسائل عن سبب استعمال الاسم useState لهذا الخطاف وليس الاسم | '''ملاحظة''': قد تتسائل عن سبب استعمال الاسم <code>useState</code> لهذا الخطاف وليس الاسم <code>createState</code>؟ الجواب هو أنَّ "إنشاء" (create) لن تكون دقيقة والسبب أنَّ الحالة تُنشَأ عندما يصيَّر المكون أول مرة فقط. في عمليات التصيير اللاحقة، يعطينا الخطاف <code>useState</code> القيمة (الحالة) الحالية وإلا لما كنا أطلقنا عليها "حالة" (state) على الإطلاق. هنالك أيضًا سبب متعلق ببدء تسمية الخطافات بالكلمة <code>use</code>، وسنتعرف عليه في توثيق "[[React/hooks rules|قواعد استعمال الخطافات]]". | ||
== قراءة الحالة == | == قراءة الحالة == | ||
إن أردنا إظهار قيمة المتغير count الحالية في صنف، | إن أردنا إظهار قيمة المتغير <code>count</code> الحالية في صنف، يمكن أن نقرأ <code>this.state.count</code>:<syntaxhighlight lang="html"> | ||
<p>You clicked {this.state.count} times</p> | <p>You clicked {this.state.count} times</p> | ||
</syntaxhighlight> | </syntaxhighlight>يمكننا استعمال المتغير <code>count</code> في الدوال مباشرةً:<syntaxhighlight lang="html"> | ||
<p>You clicked {count} times</p> | <p>You clicked {count} times</p> | ||
سطر 116: | سطر 122: | ||
== تحديث الحالة == | == تحديث الحالة == | ||
في الصنف، نحتاج إلى استدعاء this.setState() لتحديث الحالة count:<syntaxhighlight lang="html"> | في الصنف، نحتاج إلى استدعاء <code>this.setState()</code> لتحديث الحالة <code>count</code>:<syntaxhighlight lang="html"> | ||
<button onClick={() => this.setState({ count: this.state.count + 1 })}> | <button onClick={() => this.setState({ count: this.state.count + 1 })}> | ||
Click me | Click me | ||
</button> | </button> | ||
</syntaxhighlight>في الدالة، لدينا setState و count مسبقًا كمتغيرات، لذا لا تحتاج إلى this:<syntaxhighlight lang="html"> | </syntaxhighlight>في الدالة، لدينا <code>setState</code> و <code>count</code> مسبقًا كمتغيرات، لذا لا تحتاج إلى <code>this</code>:<syntaxhighlight lang="html"> | ||
<button onClick={() => setCount(count + 1)}> | <button onClick={() => setCount(count + 1)}> | ||
Click me | Click me | ||
سطر 143: | سطر 149: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* '''السطر 1''': استوردنا الخطاف useState من React. هذا يمكننا من إبقاء حالة محلية (local state) في مكون دالة. | * '''السطر 1''': استوردنا الخطاف <code>useState</code> من [[React]]. هذا يمكننا من إبقاء حالة محلية (local state) في مكون دالة. | ||
* '''السطر 4''': داخل المكون | * '''السطر 4''': داخل المكون <code>Example</code>، صرَّحنا عن متغير حالة جديد عبر استدعاء الخطاف <code>useState</code>. يعيد الخطاف زوجًا من القيم أعطينا لكل من هاتين القيمتين اسمًا. سنتحاج إلى استدعاء المتغير <code>count</code> لأنَّه يحمل عدد ضغطات الزر. هيَّأنا هذا المتغير إلى القيمة 0 عبر تمرير هذه القيمة إلى <code>useState</code>. القيمة الثانية من الزوج الذي يعيده الخطاف هي دالة بحد ذاتها. هذه الدالة تحدِّث المتغير <code>count</code>، لذا سنطلق عليها <code>setCount</code>. | ||
* '''السطر 9''': عندما يضغط المستخدم على الزر، تُستدعَى الدالة setCount مع القيمة الجديدة. ستعيد React بعد ذلك تصيير المكون Example مع تمرير القيمة count الجديدة إليه. | * '''السطر 9''': عندما يضغط المستخدم على الزر، تُستدعَى الدالة <code>setCount</code> مع القيمة الجديدة. ستعيد [[React]] بعد ذلك تصيير المكون <code>Example</code> مع تمرير القيمة <code>count</code> الجديدة إليه. | ||
قد | قد يبدو للوهلة الأولى أنَّ هذا كثير ويحتاج إلى فهم وتركيز كبيرين. لا تتسرع بالحكم. إن شعرت أنَّك قد فقدت تركيزك أثناء الشرح، حضِّر كوبًا من القهوة ثمَّ اشربه مع إعادة قراءة الشيفرات بدءًا من بداية الدرس وحاول فهم كل سطر منها. نعدك إن نسيت (مؤقتًا) كيفية عمل الحالة في الأصناف ونظرت إلى الشيفرة مجدَّدًا، فستفهمها بشكل كامل. | ||
== | === ما الذي تعنيه الأقواس المعقوفة؟ === | ||
لابد | لابد أنَّك لاحظ وجود أقواس معقوفة عند التصريح عن متغير حالة:<syntaxhighlight lang="javascript"> | ||
const [count, setCount] = useState(0); | const [count, setCount] = useState(0); | ||
</syntaxhighlight>الأسماء في القسم الأيسر ليست جزءًا من واجهة React البرمجية. يمكنك أن تسمي متغيرات الحالة الخاصة بك بأي اسم تريد:<syntaxhighlight lang="javascript"> | </syntaxhighlight>الأسماء في القسم الأيسر ليست جزءًا من واجهة [[React]] البرمجية. يمكنك أن تسمي متغيرات الحالة الخاصة بك بأي اسم تريد:<syntaxhighlight lang="javascript"> | ||
const [fruit, setFruit] = useState('banana'); | const [fruit, setFruit] = useState('banana'); | ||
</syntaxhighlight>هذه الصيغة هي إحدى صيغ | </syntaxhighlight>هذه الصيغة هي إحدى صيغ [[JavaScript]] التي يطلق عليها "[[JavaScript/Destructuring Assignment|تفكيك المصفوفات]]". إنَّها تعني أنَّنا نريد إنشاء متغيِّرين جديدين هما: <code>fruit</code> و <code>setFruit</code>، إذ يعيَّن الأول إلى أول قيمة يعيدها <code>useState</code>، والثاني إلى القيمة الثانية المعادة. هذا يكافئ الشيفرة التالية:<syntaxhighlight lang="javascript"> | ||
var fruitStateVariable = useState('banana'); // | var fruitStateVariable = useState('banana'); // يعيد زوجًا | ||
var fruit = fruitStateVariable[0]; // | var fruit = fruitStateVariable[0]; // أول عنصر من الزوج | ||
var setFruit = fruitStateVariable[1]; // | var setFruit = fruitStateVariable[1]; // ثاني عنصر من الزوج | ||
</syntaxhighlight>عندما | </syntaxhighlight>عندما صرَّحنا عن متغير حالة مع <code>useState</code>، فإنَّ هذا الخطاف يعيد زوجًا من القيم في مصفوفة بعنصرين. العنصر الأولي هو القيمة الحالية، والعنصر الثاني هو الدالة التي تمكننا من تحديثها. استعمال <code>[0]</code> و <code>[1]</code> للوصول إلى هاتين القيمتين أمرٌ مربك لأنَّ لهما معنى محدَّد. هذا هو سبب استعمالنا أسلوك [[JavaScript/Destructuring Assignment|الإسناد بالتفكيك]] عوضًا عن ذلك. | ||
'''ملاحظة''': ربما انتابك شيءٌ من الفضول حول كيفية معرفة [[React]] أي مكون يقابل <code>useState</code> لمَّا كنا نعيد تمرير أي شي مثل <code>this</code> إلى [[React]]. لا تقلق، سنجيب عن هذا السؤال وأسئلة أخرى في صفحة [[React/hooks faq#.D9.83.D9.8A.D9.81 .D8.AA.D8.B1.D8.A8.D8.B7 React .D8.A7.D8.B3.D8.AA.D8.AF.D8.B9.D8.A7.D8.A1.D8.A7.D8.AA .D8.A7.D9.84.D8.AE.D8.B7.D8.A7.D9.81.D8.A7.D8.AA .D9.85.D8.B9 .D8.A7.D9.84.D9.85.D9.83.D9.88.D9.86.D8.A7.D8.AA.|الأسئلة الشائعة حول الخطافات]]. | |||
=== إضافة: استعمال متغيرات حالة عديدة === | |||
التصريح عن متغيرات حالة كأزواج من <code>[something, setSomething]</code> هو أمر عملي وجيد، إذ يسمح لنا بإعطاء أسماء مختلفة لمتغيرات مختلفة للحالة إن أردنا استعمال أكثر من متغير حالة واحد:<syntaxhighlight lang="javascript"> | |||
function ExampleWithManyStates() { | |||
// التصريح عن متغيرات حالة عديدة | |||
const [age, setAge] = useState(42); | |||
const [fruit, setFruit] = useState('banana'); | |||
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); | |||
</syntaxhighlight>في هذا المكون في الشيفرة الآنفة، لدينا المتغيرات <code>age</code>، و <code>fruit</code>، و <code>todos</code> بوصفها متغيرات حالة، ويمكننا تحديث كل منها على حدة:<syntaxhighlight lang="javascript"> | |||
function handleOrangeClick() { | |||
// this.setState({ fruit: 'orange' }) يشبه | |||
setFruit('orange'); | |||
} | |||
</syntaxhighlight>لا يتوجب عليه استعمال عدة متغيرات حالة. متغيرات الحالة يمكنها أن تحوي كائنات ومصفوفات، لذا لا يزال بإمكانك تجميع البيانات المترابطة مع بعضها بعضًا. على أي حال، تحديث متغير حالة يستبدل دومًا قيمته بدلًا من دمجها، وهذا يخالف سلوك <code>this.setState</code> في الأصناف. | |||
أعطينا الكثير من التوصيات حول فصل متغيرات الحالة المستقلة في صفحة [[React/hooks faq|الأسئلة الشائعة حول الخطافات]]. | |||
== الخطوات التالية == | |||
تعلمنا حول أحد الخطافات التي توفرها [[React]] وهو الخطاف <code>useState</code>. في أغلب الأحيان، سنشير إليه بالاسم "خطاف الحالة" (State Hook). يسمح لنا هذا الخطاف بإضافة حالة محلية إلى مكونات دالة في [[React]] بخطوات بسيطة وشيفرة أقل. | |||
تعلمنا أيضًا القليل حول ماهية الخطافات. كما رأينا، الخطافات هي دوال تمكنك من "تعليق أو ربط" (hook into) ميزات [[React]] من مكونات دالة. أسماء الخطافات تبدأ دومًا بالكلمة <code>use</code>، وهنالك الكثير من الخطافات التي لم نرها بعد. | |||
لنكمل الآن وننتقل إلى الخطاف التالي وهو الخطاف <code>useEffect</code>. يسمح لنا هذا الخطاف بتنفيذ تأثيرات جانبية (side effects) في المكونات، وهو يشبه توابع دورة الحياة في الأصناف. | |||
== انظر أيضًا== | |||
*[[React/hooks intro|مدخل إلى الخطافات]] | |||
*[[React/hooks overview|لمحة خاطفة عن الخطافات]] | |||
*[[React/hooks effect|خطاف التأثير]] | |||
*[[React/hooks rules|قواعد استعمال الخطافات]] | |||
*[[React/hooks custom|بناء خطافات خاصة بك]] | |||
*[[React/hooks reference|مرجع إلى الواجهة البرمجية للخطافات]] | |||
*[[React/hooks faq|الأسئلة الشائعة حول الخطافات]] | |||
== مصادر== | |||
*[https://reactjs.org/docs/hooks-overview.html صفحة لمحة خاطفة عن الخطافات في توثيق React الرسمي.] | |||
[[تصنيف:React]] | |||
[[تصنيف:React Hooks]] |
المراجعة الحالية بتاريخ 11:02، 6 نوفمبر 2020
الخطافات هي إضافة جديدة إلى الإصدار 16.8 في React، إذ تسمح لك باستعمال ميزة الحالة وميزات React الأخرى دون كتابة أي صنف.
شرحت الصفحة السابقة في قسم خطاف الحالة هذا الخطاف عبر المثال التالي:
import React, { useState } from 'react';
function Example() {
// "count" التصريح عن متغير حالة جديد ندعوه
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
بادئ ذي بدء، سنتعرف على الخطافات عبر موازنة هذه الشيفرة مع الصنف المكافئ لها.
مثال عن صنف مكافئ لخطاف
إن استعملت الأصناف في React من قبل، فيجب أن تكون الشيفرة التالية مألوفةً لديك:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
الحالة الأولية تكون { count: 0 }
، ونعمل على زيادة state.count
عندما يضغط المستخدم على زر يستدعي this.setState()
. سنستعمل أجزاء من هذا الصنف في أقسام لاحقة من هذه الصفحة.
ملاحظة: قد تتساءل عن سبب استعمالنا مثال عدَّادٍ هنا عوضَ استعمال مثال أكثر واقعية. يكمن السبب في أنَّ هذا المثال البسيط يساعدنا على التركيز على الواجهة البرمجية في بداية رحلتنا مع الخطافات.
الخطافات ومكونات دالة
لنتذكر سويةً، تبدو مكونات دالة في React بالشكل:
const Example = (props) => {
// تستطيع استعمال الخطافات هنا
return <div />;
}
أو الشكل التالي:
function Example(props) {
// تستطيع استعمال الخطافات هنا
return <div />;
}
ربما كنت تعرف مسبقًا أنَّ هذه المكونات هي "مكونات عديمة الحالة" (stateless components). نعرِّف الآن إمكانية استعمال حالة React من هذه المكونات، لذا نفضل استعمال الاسم "مكونات دالة".
لا تعمل الخطافات داخل الأصناف، ولكن يمكن استعمالها بدلًا من الأصناف نفسها.
ما هو الخطاف؟
يبدأ مثالنا الجديد باستيراد الخطاف useState
من React:
import React, { useState } from 'react';
function Example() {
// ...
}
إذًا، ما هو الخطاف تحديدًا؟ الخطاف هو دالة مميزة تمكنك من "تعليق أو ربط" (hook into) ميزات React مع بعضها. على سبيل المثال، useState
هو خطاف يمكنك من إضافة حالة React إلى مكونات دالة. سنتطرق إلى الخطافات الأخرى لاحقًا.
متى يمكنني استعمال الخطاف؟
إن كتبت مكون دالة ووجدت أنَّك بحاجة إلى إضافة بعض الحالة له، فستحتاج - سابقًا قبل عصر الخطافات - إلى تحويله إلى صنف. الآن، يمكنك استعمال خطاف داخل مكون دالة موجودة وهو ما سنفعله الآن.
ملاحظة: هنالك بعض القواعد المخصَّصة تحدِّد المكان المسموح والمحظور فيه استعمال الخطافات ضمن مكون. سنشرح هذه القواعد بالتفصيل الممل في توثيق قواعد استعمال الخطافات.
التصريح عن متغير حالة
إن كنا سنستعمل صنفًا، نهيِّئ فيه الحالة count
إلى 0 عبر ضبط this.state
إلى { count: 0 }
في باني هذا الصنف:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
في مكون دالة، لا يمكننا استعمال this
، لذا لا نستطيع إسناد أو قراء this.state
. عوض ذلك، يمكننا استدعاء الخطاف useState
مباشرةً داخل المكون الخاص بنا:
import React, { useState } from 'react';
function Example() {
// "count" التصريح عن متغير حالة جديد ندعوه
const [count, setCount] = useState(0);
ماذا يفعل استدعاء useState
؟
إنَّه يصرِّح عن "متغير حالة" (state variable). هذا المتغير يدعى count
ولكن يمكننا أن ندعوه بأي اسم آخر مثل banana
. هذه هي طريقة لحفظ بعض القيم بين استدعاءات الدالة، إذ useState
هي طريقة جديدة لاستعمال الامكانيات نفسها التي توفرها this.state
في الصنف. عمومًا، المتغيرات "تختفي" عند اكتمال تنفيذ الدالة وخروجها ولكن متغيرات الحالة تحافظ على قيمتها في React.
لماذا نمرر وسيطًا إلى useState؟
الوسيط الوحيد الذي يمكن تمريره إلى الخطاف useState()
هو الحالة الأولية. خلافًا للأصناف، ليس من الضروري أن تكون الحالة كائنًا. يمكننا استعمال عدد أو سلسلة نصية إن كان ذلك ما نحتاجه. في مثالنا، نحتاج إلى عدد ليمثل عدد المرات التي ضغط فيها المستخدم على الزر، لذا مرَّرنا العدد 0 ليكون الحالة الأولية لمتغيرنا. (إن أردنا تخزين قيمتين مختلفتين في حالة، فيمكننا فعل ذلك عبر استدعاء useState()
مرتين.)
ما الذي يعيده useState
؟
إنه يعيد زوجًا من القيم: الحالة الحالية، ودالة تحدِّثها. لهذا السبب، نكتب const [count, setCount] = useState()
. هذا الأمر يشبه this.state.count
و this.setState
في الصنف، باستثناء أنَّنا نحصل عليهم كزوج من القيم. إن لم تكن الصيغة السابقة التي استعملناها مألوفة لك، فسنتطرق إليها في آخر هذه الصفحة.
الآن وقد عرفنا ما الذي يفعله الخطاف useState
، يجب أن تكون الشيفرة التالية مفهومة تمامًا لك:
import React, { useState } from 'react';
function Example() {
// "count" التصريح عن متغير حالة جديد ندعوه
const [count, setCount] = useState(0);
صرَّحنا عن متغير حالة يدعى count
، وأسندنا القيمة 0 العددية له. ستتذكر React قيمته الحالية بين عمليات إعادة التصيير، وتوفر الحالة الأحدث له للدالة. إن أردنا تحديث قيمة المتغير count
الحالية، يمكننا استدعاء useState
.
ملاحظة: قد تتسائل عن سبب استعمال الاسم useState
لهذا الخطاف وليس الاسم createState
؟ الجواب هو أنَّ "إنشاء" (create) لن تكون دقيقة والسبب أنَّ الحالة تُنشَأ عندما يصيَّر المكون أول مرة فقط. في عمليات التصيير اللاحقة، يعطينا الخطاف useState
القيمة (الحالة) الحالية وإلا لما كنا أطلقنا عليها "حالة" (state) على الإطلاق. هنالك أيضًا سبب متعلق ببدء تسمية الخطافات بالكلمة use
، وسنتعرف عليه في توثيق "قواعد استعمال الخطافات".
قراءة الحالة
إن أردنا إظهار قيمة المتغير count
الحالية في صنف، يمكن أن نقرأ this.state.count
:
<p>You clicked {this.state.count} times</p>
يمكننا استعمال المتغير count
في الدوال مباشرةً:
<p>You clicked {count} times</p>
تحديث الحالة
في الصنف، نحتاج إلى استدعاء this.setState()
لتحديث الحالة count
:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
في الدالة، لدينا setState
و count
مسبقًا كمتغيرات، لذا لا تحتاج إلى this
:
<button onClick={() => setCount(count + 1)}>
Click me
</button>
الخلاصة
لنراجع ما تعلمناه بالمرور على كل سطر من أسطر الشيفرة والتأكد من فهمنا للخطافات بشكل عام ولخطاف الحالة بشكل خاص:
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
- السطر 1: استوردنا الخطاف
useState
من React. هذا يمكننا من إبقاء حالة محلية (local state) في مكون دالة.
- السطر 4: داخل المكون
Example
، صرَّحنا عن متغير حالة جديد عبر استدعاء الخطافuseState
. يعيد الخطاف زوجًا من القيم أعطينا لكل من هاتين القيمتين اسمًا. سنتحاج إلى استدعاء المتغيرcount
لأنَّه يحمل عدد ضغطات الزر. هيَّأنا هذا المتغير إلى القيمة 0 عبر تمرير هذه القيمة إلىuseState
. القيمة الثانية من الزوج الذي يعيده الخطاف هي دالة بحد ذاتها. هذه الدالة تحدِّث المتغيرcount
، لذا سنطلق عليهاsetCount
.
- السطر 9: عندما يضغط المستخدم على الزر، تُستدعَى الدالة
setCount
مع القيمة الجديدة. ستعيد React بعد ذلك تصيير المكونExample
مع تمرير القيمةcount
الجديدة إليه.
قد يبدو للوهلة الأولى أنَّ هذا كثير ويحتاج إلى فهم وتركيز كبيرين. لا تتسرع بالحكم. إن شعرت أنَّك قد فقدت تركيزك أثناء الشرح، حضِّر كوبًا من القهوة ثمَّ اشربه مع إعادة قراءة الشيفرات بدءًا من بداية الدرس وحاول فهم كل سطر منها. نعدك إن نسيت (مؤقتًا) كيفية عمل الحالة في الأصناف ونظرت إلى الشيفرة مجدَّدًا، فستفهمها بشكل كامل.
ما الذي تعنيه الأقواس المعقوفة؟
لابد أنَّك لاحظ وجود أقواس معقوفة عند التصريح عن متغير حالة:
const [count, setCount] = useState(0);
الأسماء في القسم الأيسر ليست جزءًا من واجهة React البرمجية. يمكنك أن تسمي متغيرات الحالة الخاصة بك بأي اسم تريد:
const [fruit, setFruit] = useState('banana');
هذه الصيغة هي إحدى صيغ JavaScript التي يطلق عليها "تفكيك المصفوفات". إنَّها تعني أنَّنا نريد إنشاء متغيِّرين جديدين هما: fruit
و setFruit
، إذ يعيَّن الأول إلى أول قيمة يعيدها useState
، والثاني إلى القيمة الثانية المعادة. هذا يكافئ الشيفرة التالية:
var fruitStateVariable = useState('banana'); // يعيد زوجًا
var fruit = fruitStateVariable[0]; // أول عنصر من الزوج
var setFruit = fruitStateVariable[1]; // ثاني عنصر من الزوج
عندما صرَّحنا عن متغير حالة مع useState
، فإنَّ هذا الخطاف يعيد زوجًا من القيم في مصفوفة بعنصرين. العنصر الأولي هو القيمة الحالية، والعنصر الثاني هو الدالة التي تمكننا من تحديثها. استعمال [0]
و [1]
للوصول إلى هاتين القيمتين أمرٌ مربك لأنَّ لهما معنى محدَّد. هذا هو سبب استعمالنا أسلوك الإسناد بالتفكيك عوضًا عن ذلك.
ملاحظة: ربما انتابك شيءٌ من الفضول حول كيفية معرفة React أي مكون يقابل useState
لمَّا كنا نعيد تمرير أي شي مثل this
إلى React. لا تقلق، سنجيب عن هذا السؤال وأسئلة أخرى في صفحة الأسئلة الشائعة حول الخطافات.
إضافة: استعمال متغيرات حالة عديدة
التصريح عن متغيرات حالة كأزواج من [something, setSomething]
هو أمر عملي وجيد، إذ يسمح لنا بإعطاء أسماء مختلفة لمتغيرات مختلفة للحالة إن أردنا استعمال أكثر من متغير حالة واحد:
function ExampleWithManyStates() {
// التصريح عن متغيرات حالة عديدة
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
في هذا المكون في الشيفرة الآنفة، لدينا المتغيرات age
، و fruit
، و todos
بوصفها متغيرات حالة، ويمكننا تحديث كل منها على حدة:
function handleOrangeClick() {
// this.setState({ fruit: 'orange' }) يشبه
setFruit('orange');
}
لا يتوجب عليه استعمال عدة متغيرات حالة. متغيرات الحالة يمكنها أن تحوي كائنات ومصفوفات، لذا لا يزال بإمكانك تجميع البيانات المترابطة مع بعضها بعضًا. على أي حال، تحديث متغير حالة يستبدل دومًا قيمته بدلًا من دمجها، وهذا يخالف سلوك this.setState
في الأصناف.
أعطينا الكثير من التوصيات حول فصل متغيرات الحالة المستقلة في صفحة الأسئلة الشائعة حول الخطافات.
الخطوات التالية
تعلمنا حول أحد الخطافات التي توفرها React وهو الخطاف useState
. في أغلب الأحيان، سنشير إليه بالاسم "خطاف الحالة" (State Hook). يسمح لنا هذا الخطاف بإضافة حالة محلية إلى مكونات دالة في React بخطوات بسيطة وشيفرة أقل.
تعلمنا أيضًا القليل حول ماهية الخطافات. كما رأينا، الخطافات هي دوال تمكنك من "تعليق أو ربط" (hook into) ميزات React من مكونات دالة. أسماء الخطافات تبدأ دومًا بالكلمة use
، وهنالك الكثير من الخطافات التي لم نرها بعد.
لنكمل الآن وننتقل إلى الخطاف التالي وهو الخطاف useEffect
. يسمح لنا هذا الخطاف بتنفيذ تأثيرات جانبية (side effects) في المكونات، وهو يشبه توابع دورة الحياة في الأصناف.
انظر أيضًا
- مدخل إلى الخطافات
- لمحة خاطفة عن الخطافات
- خطاف التأثير
- قواعد استعمال الخطافات
- بناء خطافات خاصة بك
- مرجع إلى الواجهة البرمجية للخطافات
- الأسئلة الشائعة حول الخطافات