اذهب إلى المحتوى

تنفيذ التفاعل في تطبيق React: الأحداث والحالة


Ola Abbas

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

معالجة الأحداث

إذا استخدَمت لغة جافاسكربت الصرفة Vanilla JavaScript سابقًا، فلا بدّ أنك معتاد على وجود ملف جافاسكربت منفصل، إذ يمكنك الاستعلام عن بعض عُقد DOM وربط المستمعين بها كما يلي:

const btn = document.querySelector('button');

btn.addEventListener('click', () => {
  alert("hi!");
});

يمكننا في React كتابة معالِجات الأحداث مباشرةً للعناصر الموجودة في JSX كما يلي:

<button
  type="button"
  onClick={() => alert("hi!")}
>
  Say hi!
</button>

يمكن أن يُعَدّ ذلك أمرًا غير مناسب لنصائح أفضل الممارسات التي تميل إلى عدم استخدام معالِجات الأحداث المُضمَّنة في HTML، ولكن تذكر أنّ صيغة JSX هي جزء من لغة جافاسكربت.

أضفنا في المثال السابق السمة onClick إلى عنصر الزر <button>، وقيمة هذه السمة هي دالة تشغّل تنبيهًا بسيطًا، إذ تملك السمة onClick معنًى خاصًا هنا، فهي تخبر React بتشغيل دالة معيّنة عندما ينقر المستخدِم على الزر، كما يجب ملاحظة بعض الأشياء الأخرى، وهي:

  • تُعَدّ طبيعة اسم السمة onClick ذات حالة الجَمل Camel-cased مهمةً، إذ لن تتعرف صيغة JSX على السمة onclick، لأن هذه الكلمة محجوزة في لغة جافاسكربت، وتُستخدَم لغرض مختلف يمثل خاصيات معالِج الحدث onclick المعيارية.
  • تتبع جميع أحداث المتصفح هذا التنسيق في صيغة JSX باستخدام الجزء on متبوعًا باسم الحدث.

لنطبّق هذه الملاحظات على تطبيقنا بدءًا من المكوِّن Form.js.

معالجة إرسال النموذج

أنشئ دالةً بالاسم handleSubmit()‎ في الجزء العلوي من دالة المكوِّن Form()‎، إذ يجب على هذه الدالة منع سلوك الحدث submit الافتراضي، ثم يجب إطلاق تنبيه alert()‎ بالذي تريده كما يلي:

function handleSubmit(e) {
  e.preventDefault();
  alert('Hello, world!');
}

يمكنك استخدام هذه الدالة من خلال إضافة السمة onSubmit إلى العنصر <form>، وضبط قيمتها على الدالة handleSubmit كما يلي:

<form onSubmit={handleSubmit}>

إذا عدت إلى متصفحك ونقرت على زر "الإضافة Add"، فسيعرض المتصفح مربع حوار تنبيه يحتوي على الرسالة "Hello, world!‎" أو أيّ شيء آخر اخترته.

خاصيات رد النداء

يندر اقتصار التفاعل على مكوِّن واحد فقط في تطبيقات React، إذ ستؤثِّر الأحداث التي تحدث في أحد المكوِّنات على أجزاء أخرى من التطبيق، فإذا كان لدينا القدرة على إنشاء مهام جديدة مثلًا، فستؤثِّر الأشياء التي تحدث في المكوِّن <Form /‎> على القائمة المُصيَّرة في المكوِّن <App /‎>.

نريد أن تساعدنا الدالة handleSubmit()‎ في إنشاء مهمة جديدة، لذلك سنحتاج إلى طريقة لتمرير المعلومات من المكوِّن <Form /‎> إلى المكوِّن <App /‎>، فلا يمكننا تمرير البيانات من الابن إلى الأب بالطريقة نفسها التي نمرر بها البيانات من الأب إلى الابن باستخدام الخاصيات Props المعيارية، إذ يمكننا بدلًا من ذلك كتابة دالة في المكوِّن <App /‎> تتوقع بعض البيانات من نموذجنا بوصفها دخلًا لها، ثم تمرير هذه الدالة إلى المكوِّن <Form /‎> بوصفها خاصيةً، وتُسمَّى هذه الدالة التي تُعامَل على أنها خاصية بخاصية رد النداء Callback Prop، إذ يمكننا استدعاء خاصية رد النداء ضمن المكوِّن <Form /‎> لإرسال البيانات الصحيحة إلى المكوِّن <App /‎>.

معالجة إرسال النموذج باستخدام خاصيات رد النداء

أنشئ دالةً بالاسم addTask()‎ في الجزء العلوي من دالة المكوِّن App()‎، بحيث تحتوي هذه الدالة على معامِل واحد هو name:

function addTask(name) {
  alert(name);
}

سنمرِّر بعد ذلك الدالة addTask()‎ إلى المكوِّن <Form /‎> بوصفها خاصيةً، إذ يمكن أن تحمل الخاصية أيّ اسم تريده، ولكن اختر اسمًا تفهمه لاحقًا مثل الاسم addTask الذي يتطابق مع اسم الدالة ومع ما ستفعله، وهنا يجب تعديل استدعاء المكوِّن <Form /‎> كما يلي:

<Form addTask={addTask} />

أخيرًا، يمكنك استخدام هذه الخاصية ضمن الدالة handleSubmit()‎ في المكوِّن <Form /‎> كما يلي:

function handleSubmit(e) {
  e.preventDefault();
  props.addTask("Say hello!");
}

سيؤدي النقر على زر "الإضافة Add" في متصفحك إلى إثبات عمل دالة رد النداء addTask()‎، لكن سيكون جيدًا أن نحصل على تنبيه لإظهار ما نكتبه في حقل الإدخال، وهذا ما سنفعله لاحقًا.

ملاحظة: سمّينا خاصية رد النداء بالاسم addTask لتسهيل فهم ما ستفعله هذه الخاصية، ومن الاصطلاحات الشائعة الأخرى التي قد تصادفها في شيفرة React هي أن تُسبَق أسماء خاصيات رد النداء بالكلمة on متبوعةً باسم الحدث الذي سيؤدي إلى تشغيلها، إذ يمكننا مثلًا تسمية خاصية بالاسم onSubmit مع القيمة addTask.

الحالة والخطاف useState

استخدَمنا حتى الآن الخاصيات لتمرير البيانات عبر المكوّنات، ولكننا نحتاج إلى شيء آخر عند تعاملنا مع دخل المستخدِم وتحديثات البيانات.

تأتي الخاصيات من أب المكوِّن، فلن يرث المكوِّن <Form /‎> مثلًا اسمًا جديدًا للمهمة، إذ يتواجد العنصر <input /‎> مباشرةً ضمن المكوِّن <Form /‎>، لذا سيكون هذا المكوِّن مسؤولًا مباشرةً عن إنشاء هذا الاسم الجديد، كما لا يمكننا الطلب من المكوِّن <Form /‎> إنشاء خاصياته تلقائيًا، ولكن يمكننا أن نطلب منه تتبع بعض بياناته، إذ تسمى هذه البيانات التي يمتلكها المكوِّن نفسه بالحالة State، والتي تُعَدّ أداةً قويةً أخرى من React، لأن المكوّنات لا تمتلك الحالة فحسب، وإنما يمكنها تحديثها لاحقًا، في حين لا يمكن تحديث الخاصيات التي يتلقاها المكوِّن، فهي للقراءة فقط.

توفِّر React مجموعةً متنوعةً من الدوال الخاصة التي تسمح لنا بتوفير إمكانات جديدة للمكوّنات مثل الحالة، إذ تُسمَّى هذه الدوال بالخطّافات Hooks، والخطّاف useState -كما يوحي اسمه- هو بالضبط الذي نحتاجه لإعطاء مكوِّننا حالةً، وهنا يجب استيراد خطّاف React من الوحدة react لاستخدامه، لذا عدِّل السطر الأول في الملف Form.js ليصبح كما يلي:

import React, { useState } from "react";

يسمح لنا ذلك باستيراد الدالة useState()‎، واستخدامها في أيّ مكان من هذا الملف، كما تنشئ هذه الدالة حالةً لمكوِّن، ويحدِّد معامِلها الوحيد القيمة الأولية لتلك الحالة، كما تُعيد هذه الدالة الحالةَ ودالةً يمكن استخدامها لتحديث الحالة لاحقًا، ولنجرب ذلك أولًا من خلال إنشاء الحالة name ودالةً لتحديث هذه الحالة، لذا اكتب السطر التالي قبل الدالة handleSubmit()‎ ضمن الدالة Form()‎:

const [name, setName] = useState('Use hooks!');

يحدُث ما يلي في السطر السابق:

  • ضبط قيمة الحالة name الأولية على القيمة "Use hooks!‎".
  • تعريف دالة بالاسم setName()‎ وظيفتها تعديل الحالة name.
  • تعيد الدالة useState()‎ الشيئين السابقين، لذا فإننا نستخدِم عملية هدم المصفوفات Array Destructuring لإسنادهما إلى متغيرَين منفصلين.

حالة القراءة

يمكنك رؤية الحالة name قيد التشغيل مباشرةً، لذا أضِف السمة value إلى العنصر input في النموذج، واضبط قيمتها لتكون name، إذ سيصيِّر متصفحك التعليمة "Use hooks!‎" في العنصر input.

<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
/>

عدِّل بعد ذلك التعليمة "Use hooks!‎" إلى سلسلة نصية فارغة، فهذا ما نريده لحالتنا الأولية كما يلي:

const [name, setName] = useState('');

قراءة دخل المستخدم

يجب التقاط دخل المستخدِم الذي يكتبه قبل تمكننا من تغيير قيمة الحالة nameمن خلال الاستماع إلى الحدث onChange، فلنكتب الدالة handleChange()‎، ونستمع إليها على الوسم <input /‎>.

// قُرب أعلى المكوِّن ‫`Form`
function handleChange(e) {
  console.log("Typing!");
}

// بعد تعليمة‫ `return`
<input
  type="text"
  id="new-todo-input"
  className="input input__lg"
  name="text"
  autoComplete="off"
  value={name}
  onChange={handleChange}
/>

لن تتغير قيمة الدخل أثناء الكتابة حاليًا، ولكن سيطبع متصفحك الكلمة "Typing!" على طرفية جافاسكربت (نافذة console)، وبالتالي سنعلم أنّ مستمع الحدث متصل بالدخل، كما يمكنك تغيير قيمة الدخل من خلال استخدام الدالة handleChange()‎ لتحديث الحالة name.

يمكن قراءة محتويات حقل الإدخال عند تغييرها من خلال الوصول إلى الخاصية value الخاصة بالعنصر input عن طريق قراءة القيمة e.target.value ضمن الدالة handleChange()‎، إذ يمثِّل e.target العنصر الذي أَطلق الحدث change، وهو العنصر input، وبالتالي تكون الخاصية value هي النص الموجود ضمنها، كما يمكنك تنفيذ التابع console.log()‎ على هذه القيمة لرؤيتها في طرفية متصفحك كما يلي:

function handleChange(e) {
  console.log(e.target.value);
}

حالة التحديث

يجب تخزين حالة name المحدَّثة مع تغير قيمة الدخل، لذا غيِّر التابع console.log()‎ إلى setName()‎ كما يلي:

function handleChange(e) {
  setName(e.target.value);
}

يجب الآن تعديل الدالة handleSubmit()‎ لتستدعي الخاصية props.addTask مع الحالة name على أساس وسيط، مما يؤدي إلى إرسال المهمة مرةً أخرى إلى المكوِّن App، لنتمكن من إضافتها إلى قائمة المهام لاحقًا، كما يجب مسح الدخل بعد إرسال النموذج، لذلك سنستدعي الدالة setName()‎ مرةً أخرى مع سلسلة نصية فارغة.

function handleSubmit(e) {
  e.preventDefault();
  props.addTask(name);
  setName("");
}

أخيرًا، يمكنك كتابة شيء ما في حقل الإدخال في متصفحك والنقر فوق زر "Add"، وبالتالي سيظهر كل ما كتبته في مربع حوار التنبيه، والآن يجب أن يكون الملف Form.js كما يلي:

import React, { useState } from "react";

function Form(props) {
  const [name, setName] = useState("");

  function handleChange(e) {
    setName(e.target.value);
  }

  function handleSubmit(e) {
    e.preventDefault();
    props.addTask(name);
    setName("");
  }
  return (
    <form onSubmit={handleSubmit}>
      <h2 className="label-wrapper">
        <label htmlFor="new-todo-input" className="label__lg">
          What needs to be done?
        </label>
      </h2>
      <input
        type="text"
        id="new-todo-input"
        className="input input__lg"
        name="text"
        autoComplete="off"
        value={name}
        onChange={handleChange}
      />
      <button type="submit" className="btn btn__primary btn__lg">
        Add
      </button>
    </form>
  );
}

export default Form;

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

إضافة مهمة

أصبحنا الآن جاهزين لكتابة دوال تسمح للمستخدِم بإضافة مهمة جديدة من متصفحه بعد أن تدربنا على الأحداث وخاصيات رد النداء والخطّافات.

استخدام المهام بوصفها حالات

استورِد الخطّاف useState إلى الملف App.js لتتمكن من تخزين المهام في حالة ما من خلال تعديل سطر استيراد React إلى ما يلي:

import React, { useState } from "react";

نريد تمرير الخاصية props.tasks إلى الخطّاف useState()‎، إذ ستحتفظ هذه الخاصية بحالة الخطّاف الأولية، لذا أضف السطر التالي في الجزء العلوي من تعريف الدالة App()‎:

const [tasks, setTasks] = useState(props.tasks);

يمكننا الآن تغيير ربط قائمة المهام taskList لتكون نتيجةً لربط tasks بدلًا من props.tasks، إذ يجب أن يبدو تصريح الثابت taskList كما يلي:

const taskList = tasks.map(task => (
    <Todo
        id={task.id}
        name={task.name}
        completed={task.completed}
        key={task.id}
      />
    )
  );

إضافة مهمة

لدينا الآن الخطّاف setTasks الذي يمكننا استخدامه في الدالة addTask()‎ لتحديث قائمة المهام، ولكن هناك مشكلة أنه لا يمكننا فقط تمرير الوسيط name الخاص بالدالة addTask()‎ إلى الخطاف setTasks، لأنّ tasks هي مصفوفة من الكائنات؛ أما الوسيط name، فهو سلسلة نصية، وبالتالي ستكون السلسلة النصية مكان المصفوفة.

يجب أولًا وضع الوسيط name في كائن له بنية مهامنا الحالية نفسها، إذ سننشئ ضمن الدالة addTask()‎ الكائن newTask لإضافته إلى المصفوفة، ويجب بعد ذلك إنشاء مصفوفة جديدة مع إضافة هذه المهمة الجديدة إليها، ثم تحديث حالة بيانات المهام إلى هذه الحالة الجديدة من خلال استخدام صيغة الانتشار Spread Syntax لنسخ المصفوفة الحالية، وإضافة الكائن في النهاية، ثم تمرير هذه المصفوفة إلى الدالة setTasks()‎ لتحديث الحالة، وبالتالي يجب أن تصبح الدالة addTask()‎ كما يلي:

function addTask(name) {
  const newTask = { id: "id", name: name, completed: false };
  setTasks([...tasks, newTask]);
}

يمكنك الآن استخدام المتصفح لإضافة مهمة إلى بياناتنا، لذا اكتب أيّ شيء تريده في النموذج، وانقر زر الإضافة Add أو اضغط على مفتاح Enter من لوحة المفاتيح، إذ سترى عنصر المهام الجديد يظهَر في واجهة المستخدِم، لكن هناك مشكلة أخرى تتمثّل بإعطاء الدالة addTask()‎ المعرّف id نفسه لكل مهمَّة، إذ يُعَدّ ذلك أمرًا سيئًا لإمكانية الوصول، كما يجعل التمييز بين المهام المستقبلية أمرًا مستحيلًا على React باستخدام الخاصية key، إذ ستعطيك React تحذيرًا في طرفية أدوات التطوير DevTools مثل رسالة التحذير التالية: "Warning: Encountered two children with the same key…‎".

يجب إصلاح هذه المشكلة، كما يُعَدّ إنشاء معرّفات فريدة مشكلةً صعبةً، وهي مشكلة كتَب لها مجتمع جافاسكربت بعض المكتبات المفيدة من أجلها، إذ سنستخدِم حاليًا المكتبة nanoid لأنها صغيرة الحجم وتعمل جيدًا، لذا تأكّد من أنك في المجلد الجذر لتطبيقك وشغّل الأمر التالي في طرفيتك:

npm install nanoid

وفي ملاحظة مهمة، إذا أردتَ استخدام مدير الحزم yarn، فيجب كتابة الأمر: yarn add nanoid.

يمكننا الآن استيراد المكتبة nanoid في الجزء العلوي من الملف App.js لنتمكّن من استخدامها لإنشاء معرِّفات فريدة لمهامنا الجديدة، لذا ضمّن أولًا سطر الاستيراد التالي في أعلى الملف App.js:

import { nanoid } from "nanoid";

لنحدّث الآن الدالة addTask()‎ بحيث يصبح كل معرِّف مهمة مؤلفًا من البادئة todo-‎ بالإضافة إلى سلسلة نصية فريدة تنشئها المكتبة nanoid، لذا عدِّل تصريح الثابت newTask إلى ما يلي:

const newTask = { id: "todo-" + nanoid(), name: name, completed: false };

احفظ كل شيء، وجرب تطبيقك مرةً أخرى، إذ يمكنك الآن إضافة المهام دون تلقِّي هذا التحذير بشأن المعرِّفات المُكرَّرة.

عد المهام

يمكننا الآن إضافة مهام جديدة، ولكن هناك مشكلة تتمثَّل بقراءة العنوان ثلاث مهام متبقية، بغض النظر عن عدد المهام، إذ يمكن إصلاح ذلك عن طريق حساب طول قائمة المهام taskList وتغيير نص العنوان وفقًا لذلك، لذا أضف ما يلي ضمن تعريف الدالة App()‎ قبل تعليمة return:

const headingText = `${taskList.length} tasks remaining`;

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

const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task';
const headingText = `${taskList.length} ${tasksNoun} remaining`;

يمكنك الآن استبدال المتغير headingText بمحتوى نص عنوان القائمة، لذا عدِّل العنصر <h2> ليصبح كما يلي:

<h2 id="list-heading">{headingText}</h2>

إكمال مهمة

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

إثبات الخطأ

لنكتب الدالة toggleTaskCompleted()‎ في المكوِّن App()‎، إذ سيكون لهذه الدالة المعامِل id، لكننا لن نستخدِمها حاليًا، إذ سنسجِّل الآن المهمة الأولى في المصفوفة في الطرفية، وسنفحص ما يحدث عندما نحدِّدها أو نلغي تحديدها في متصفحنا، لذا أضف ما يلي قبل التصريح عن الثابت taskList مباشرةً:

function toggleTaskCompleted(id) {
  console.log(tasks[0])
}

سنضيف بعد ذلك الخاصية toggleTaskCompleted إلى خاصيات كل مكوّن من مكوّنات <Todo /‎> المُصيَّرة ضمن taskList كما يلي:

const taskList = tasks.map(task => (
  <Todo
      id={task.id}
      name={task.name}
      completed={task.completed}
      key={task.id}
      toggleTaskCompleted={toggleTaskCompleted}
  />
));

انتقل إلى المكوِّن Todo.js وأضف معالِج الحدث onChange إلى العنصر <input /‎> الذي يجب أن يستخدِم دالةً مجهولةً لاستدعاء الخاصية props.toggleTaskCompleted()‎ مع المعامِل props.id، إذ يجب أن يكون العنصر <input /‎> الآن كما يلي:

<input
  id={props.id}
  type="checkbox"
  defaultChecked={props.completed}
  onChange={() => props.toggleTaskCompleted(props.id)}
/>

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

Object { id: "task-0", name: "Eat", completed: true }

يُلغَى تحديد مربع الاختيار في المتصفح، لكن تخبرنا الطرفية بأن المهمَّة Eat لا تزال مكتملةً، وسنصلح ذلك لاحقًا.

مزامنة المتصفح مع بياناتنا

لنَعُد إلى الدالة toggleTaskCompleted()‎ في الملف App.js، إذ نريدها أن تغيِّر الخاصية completed للمهمَّة التي أُلغِي تحديدها فقط، وترك المهام الأخرى كما هي، لذلك سنطبّق التابع map()‎ على قائمة المهام وسنغيّر القائمة التي أكملناها فقط، لذا عدِّل الدالة toggleTaskCompleted()‎ إلى ما يلي:

function toggleTaskCompleted(id) {
  const updatedTasks = tasks.map(task => {
    // إذا كان لهذه المهمة معرِّف المهمة المُعدَّلة نفسه
    if (id === task.id) {
      // استخدم انتشار الكائن لإنشاء كائن جديد
      // ‫عُدِّلت الخاصية `completed` الخاصة به
      return {...task, completed: !task.completed}
    }
    return task;
  });
  setTasks(updatedTasks);
}

عرّفنا الثابت updatedTasks الذي يمر على عناصر المصفوفة tasks الأصلية، فإذا طابقت خاصية معرّفُ id المهمة المعرّفَ id المقدَّم للدالة، فسنستخدِم صيغة انتشار الكائن Object Spread Syntax لإنشاء كائن جديد، وسنبدّل إلى الخاصية checked لهذا الكائن قبل إعادته؛ أما إذا لم يتطابقا، فسنعيد الكائن الأصلي.

نستدعي بعد ذلك الدالة setTasks()‎ مع هذه المصفوفة الجديدة لتحديث الحالة.

حذف مهمة

سيتَّبع حذف مهمة نمطًا مشابهًا لتبديل حالتها المكتملة، إذ يجب تعريف دالة لتحديث الحالة، ثم تمرير هذه الدالة إلى المكوِّن <Todo /‎> بوصفها خاصيةً، واستدعاؤها عند حدوث الحدث الصحيح.

خاصية رد النداء deleteTask

سنكتب الدالة deleteTask()‎ في المكوِّن App، إذ ستأخذ هذه الدالة المعامِل id مثل الدالة toggleTaskCompleted()‎، وسنسجّل هذا المعرِّف في الطرفية، لذا أضف ما يلي بعد الدالة toggleTaskCompleted()‎:

function deleteTask(id) {
  console.log(id)
}

أضف بعد ذلك خاصية رد نداء أخرى إلى مصفوفة مكوّنات <Todo /‎> كما يلي:

const taskList = tasks.map(task => (
  <Todo
    id={task.id}
    name={task.name}
    completed={task.completed}
    key={task.id}
    toggleTaskCompleted={toggleTaskCompleted}
    deleteTask={deleteTask}
  />
));

يجب استدعاء الدالة props.deleteTask()‎ في الملف Todo.js عند الضغط على زر الحذف Delete، كما تحتاج الدالة deleteTask()‎ إلى معرفة معرِّف المهمَّة التي ستستدعيها لتتمكّن من حذف المهمَّة الصحيحة من الحالة، لذا عدِّل زر الحذف Delete ضمن الملف Todo.js كما يلي:

<button
  type="button"
  className="btn btn__danger"
  onClick={() => props.deleteTask(props.id)}
>
  Delete <span className="visually-hidden">{props.name}</span>
</button>

إذا نقرتَ الآن على أيّ من أزرار الحذف Delete في التطبيق، فيجب أن تسجِّل طرفية المتصفح معرّف المهمَّة المرتبطة به.

حذف المهام من الحالة وواجهة المستخدم

يمكننا الآن استدعاء الخطّاف setTasks()‎ في الدالة deleteTask()‎ استدعاءً صحيحًا لحذف هذه المهمة فعليًا من حالة التطبيق وحذفها مرئيًا من واجهة مستخدِم التطبيق، وبما أنّ الخطّاف setTasks()‎ يتوقع مصفوفةً بوصفها وسيطًا له، فيجب تزويده بمصفوفة جديدة تنسخ المهام الحالية باستثناء المهمة التي يتطابق معرِّفها مع معرِّف المهمَّة المُمرَّرة إلى الدالة deleteTask()‎.

يمكننا الآن استخدام التابع Array.prototype.filter()‎، إذ يمكننا اختبار كل مهمة، واستبعاد مهمة من المصفوفة الجديدة إذا تطابقت خاصيتها id مع المعامِل id المُمرَّر إلى الدالة deleteTask()‎، لذا عدِّل الدالة deleteTask()‎ ضمن الملف App.js كما يلي:

function deleteTask(id) {
  const remainingTasks = tasks.filter(task => id !== task.id);
  setTasks(remainingTasks);
}

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

الخلاصة

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

ترجمة -وبتصرُّف- للمقال React interactivity: Events and state.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...