ابراهيم الخضور

سنتعرف فيما سيأتي على أهم موضوع في هذا المنهاج، بالتحديد على مكتبة React. لنبدأ إذًا بكتابة تطبيق بسيط يمهد الطريق لفهم المبادئ الأساسية لهذه المكتبة. سنستعمل الأداة create-react-app التي ستسهل علينا كثيرا التعامل مع الملفات الأساسية لمشروع React، فمن الممكن تثبيت هذه الأداة على حاسوبك طالما أن نسخة الأداة npm التي ثُبِّتت مع Node على الأقل 5.3، وبالطبع يبقى موضوع استخدام هذه الأداة اختيارًا شخصيًا. لننشئ الآن تطبيقًا باسم (Part1) ولننتقل إلى المجلد الذي يضم هذا التطبيق من خلال تنفيذ الشيفرة التالية:

$ npx create-react-app part1
$ cd part1

سيبدأ كل سطر في هذا التطبيق وفي كل تطبيق بالرمز $ ضمن واجهة الطرفية. لن تضطر إلى كتابة هذا الرمز باستمرار لأنه في الواقع سيمثل مَحثَّ سطر الأوامر. سنشغل التطبيق بكتابة الأمر التالي:

$ npm start

سيعمل التطبيق بشكل افتراضي على الخادم المحلي (LocalHost) لجهازك عند المنفذ (Port) رقم 3000 وعلى العنوان http://localhost:3000 وسيفتح متصفحك الافتراضي آليًا صفحة الويب. افتح طرفية المتصفح مباشرةً وافتح أيضًا محرر نصوص لتتمكن من عرض الشيفرة وصفحة الويب في نفس الوقت على شاشتك.

create_react_app_console_001.png

يحتوي المجلد src على شيفرة التطبيق. سنبسِّط شيفرة التطبيق المكتوبة في الملف index.js التي ستظهر كالتالي:

import React from 'react'
import ReactDOM from 'react-dom'

const App = () => (
  <div>
    <p>Hello world</p>
  </div>
)

ReactDOM.render(<App />, document.getElementById('root'))

يمكنك حذف الملفات التالية فلن تحتاجها حاليًا في التطبيق:

  • App.js
  • App.css
  • App.test.js
  • logo.svg
  • setupTests.js
  • service Worker.js

المكوِّنات Components

يعّرف الملف index.js مكوّنَ React يدعى App، سيصيّره السطر الأخير من الشيفرة إلى عنصرdiv الذي ستجده معرفًا في الملف public/index.html ويحمل القيمة "root" في الخاصية id.

ReactDOM.render(<App />, document.getElementById('root'))

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

const App = () => (
  <div>
    <p>Hello world</p>
  </div>
)

إن كان تخمينك صحيحًا، سيُصيَّر المكوِّن إلى عنصرdiv يضم عنصرp يحوي داخله العبارة "Hello Word". في الواقع عُرّف المكوِّن على شكل دالة JavaScript، فتمثل الشيفرة التالية دالة لا تمتلك أية مُعاملات:

() => (
  <div>
    <p>Hello world</p>
  </div>
)

ثم تُسند الدالة بعدها إلى متغيّر ثابت القيمة (Constant Variable) يدعى App:

const App = ...

تُعرّف الدوال في JavaScript بطرق عدة، سنستخدم هنا الدوال السهمية التي وصِّفت في النسخ الأحدث من JavaScript تحت عنوان ECMAScript 6 والذي يدعى أيضًا ES6. ونظرًا لاحتواء الدالة على عبارة وحيدة، فقد استخدمنا طريقة مختصرة في تمثيلها كما تظهرها الشيفرة التالية:

const App = () => {
  return (
    <div>
      <p>Hello world</p>
    </div>
  )
}

لاحظ أنّ الدالة قد أعادت قيمة العبارة التي احتوتها. قد تحتوي الدالة التي تعرِّف المكوِّن أية شيفرة مكتوبة بلغة JavaScript. عدِّل الشيفرة لتصبح على النحو التالي وراقب ما الذي سيتغير على شاشة الطرفية:

const App = () => {
  console.log('Hello from component')
  return (
    <div>
      <p>Hello world</p>
    </div>
  )
}

من الممكن أيضًا تصيير المحتويات الديناميكية إن وجدت داخل المكوِّن، عدِّل الشيفرة لتصبح كالتالي:

const App = () => {
  const now = new Date()
  const a = 10
  const b = 20

  return (
    <div>
      <p>Hello world, it is {now.toString()}</p>
      <p>
        {a} plus {b} is {a + b}
      </p>
    </div>
  )
}

تُنفَّذ شيفرة JavaScript الموجودة داخل الأقواس المعقوصة (curly braces) وتظهر نتيجتها ضمن أسطر HTML التي يولدها المكوِّن.

JSX

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

import React from 'react'
import ReactDOM from 'react-dom'

const App = () => {
  const now = new Date()
  const a = 10
  const b = 20
  return React.createElement(
    'div',
    null,
    React.createElement(
      'p', null, 'Hello world, it is ', now.toString()
    ),
    React.createElement(
      'p', null, a, ' plus ', b, ' is ', a + b
    )
  )
}

ReactDOM.render(
  React.createElement(App, null),
  document.getElementById('root')
)

تُرجم التطبيق باستخدام Babel. تترجم المشاريع التي أُنشئت باستخدام create-react-app أليًا، وسنطّلع أكثر على هذا الموضوع في القسم 7 من هذا المنهاج. من الممكن أيضًا كتابة تطبيقات React مستخدمين شيفرة JavaScript صرفة دون اعتماد JSX، لكن لن يرغب أحد بفعل ذلك.

تتشابه من الناحية العملية كل من JSX و HTML، ويكمن الاختلاف البسيط بينهما في سهولة إدراج شيفرة JavaScript ضمن أقواس معقوصة وتنفيذها ضمن JSX. إن فكرة JSX مشابهة لفكرة العديد من اللغات التي تعتمد على القوالب مثل لغة Thymeleaf التي تستخدم مع Java Spring على الخادم. وهي تشابه XML في أن معرِّفي البداية والنهاية لكل عنصر يجب أن يكونا موجودين. فعلى سبيل المثال يمكن كتابة عنصر السطر الجديد <br> في HTML على النحو:

<br>

لكن عندما نستخدم JSX، لابد من إغلاق العنصر أي وضع معرِّف النهاية كالتالي:

<br />

المكوّنات المتعددة

سنعدّل شيفرة التطبيق على النحو التالي:

const Hello = () => {
  return (
    <div>
      <p>Hello world</p>
    </div>
  )
}

const App = () => {
  return (
    <div>
      <h1>Greetings</h1>
      <Hello />    
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

انتبه: لم ندرج الملفات الرأسية (الملفات المدرجة باستخدام تعليمة import) في الشيفرة المعروضة سابقًا ولن نفعل في المستقبل، لكن عليك إدراجها دائمًا عندما تحاول تشغيل التطبيق.

عرّفنا داخل المكوّن App مكوِّنًا جديدًا يدعى Hello استُخدم أكثر من مرة ويعتبر ذلك أمرًا طبيعيًًا:

const App = () => {
  return (
    <div>
      <h1>Greetings</h1>
      <Hello />
      <Hello />      
      <Hello />
    </div>
  )
}

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

تعتبر فكرة المكوّن الجذري (root component) الذي يمثله هنا App (المكوّن الذي يقع أعلى شجرة المكوِّنات)، نقطة قوة أخرى في React. مع ذلك سنجد في القسم 6 أنّ هذا المكوّن لن يكون جذريًّا تمامًا، لكنه سيُغلَّف ضمن مكوِّن تخديمي (utility component) مناسب.

الخصائص (Props): تمرير البيانات إلى المكوِّنات

من الممكن تمرير البيانات إلى المكوّنات مستخدمين مايسمى الخصائص (Props). سنعدّل المكوِّن Hello كالتالي:

const Hello = (props) => {  
  return (
    <div>
      <p>Hello {props.name}</p>
    </div>
  )
}

تمتلك الدالة التي تعرّف المكوِّن Hello مُعاملًا يدعى props. يستقبل المُعامل (الذي يلعب دور الوسيط هنا) كائنًا يمتلك حقولًا تمثل كل الخصائص التي عُرِّفت في المكوّن. سنستعرض ذلك من خلال الشيفرة التالية:

const App = () => {
  return (
    <div>
      <h1>Greetings</h1>
      <Hello name="George" />
      <Hello name="Daisy" />
    </div>
  )
}

لا يوجد عدد محدد من الخصائص لمكوّن ما، ويمكن أن تحمل هذه الخصائص نصوصًا جاهزةً (hard coded) أو قيمًا ناتجة عن تنفيذ عبارات JavaScript. ينبغي وضع قيم الخصائص الناتجة عن تنفيذ تلك العبارات ضمن أقواس معقوصة. سنعدل شيفرة المكوّن Hello ليمتلك خاصيتين كالتالي:

const Hello = (props) => {
  return (
    <div>
      <p>
        Hello {props.name}, you are {props.age} years old
      </p>
    </div>
  )
}

const App = () => {
  const name = 'Peter'  const age = 10
  return (
    <div>
      <h1>Greetings</h1>
      <Hello name="Maya" age={26 + 10} />
      <Hello name={name} age={age} />
    </div>
  )
}

أرسل المكوِّن App الخاصيتين على شكل قيم عائدة لمتغيّرات، كما أرسلهما أيضًا كنتيجة لتنفيذ عبارتي JavaScript، أحداهما عبارة جمع والأخرى قيمة نصية.

بعض الملاحظات

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

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

browser_fail_to_compile_002.png

سينفعك أن تكتب التعليمة ()console.log بين الحين والأخر ضمن أسطر شيفرة تطبيقك، حيث تطبع هذه التعليمة سجلات تنفيذ الشيفرة على الطرفية. وتذكر أنّ أسماء مكوِّنات React تبدأ بأحرف كبيرة (Capitals)، فلو حاولت تعريف مكوِّن بالشكل التالي:

const footer = () => {
  return (
    <div>
      greeting app created by <a href="https://github.com/mluukkai">mluukkai</a>
    </div>
  )
}

ثم استخدمته كما يلي:

const App = () => {
  return (
    <div>
      <h1>Greetings</h1>
      <Hello name="Maya" age={26 + 10} />
      <footer />
    </div>
  )
}

لن تعرض الصفحة حينها المحتوى الذي يقدمه المكوِّن Footer، بل ستنشئ عنصرًا فارغًا يحمل اسم footer. لكن إن حولت الحرف الأول من اسم المكوِّن إلى F، ستنشئ React عنصر div المعرّف ضمن المكوّن Footer وسيصيّره على الصفحة.

تذكر أن مكوِّنات React تتطلب عادة وجود عنصر جذري (root element) وحيد، فلو حاولنا تعريف المكوِّن App على سبيل المثال دون وجود العنصر div في البداية كالتالي:

const App = () => {
  return (
    <h1>Greetings</h1>
    <Hello name="Maya" age={26 + 10} />
    <Footer />
  )
}

ستكون النتيجة رسالة الخطأ التالية:

error_missing_root_element_003.png

يمكن أن نستخدم وسيلة بديلة عن العنصر الجذري تتمثل بإنشاء مصفوفة من المكوِّنات كما يلي:

const App = () => {
  return [
    <h1>Greetings</h1>,
    <Hello name="Maya" age={26 + 10} />,
    <Footer />
  ]
}

لا يعتبر -على الرغم من ذلك- تعريف العنصر الجذري ضمن المكوِّن الجذري لتطبيق ما أمرًا مفضلًا، بل ستظهر الشيفرة بمظهر سيئٍ قليلًا. ذلك أنّ العنصر الجذري المحدد مسبقًا، سيسبب زيادة في عدد عناصرdiv في شجرة DOM. يمكن تلافي هذا الأمر باستخدام الأجزاء (fragments)، أي بتغليف العناصر ليعيدها المكوِّن على شكل عناصر فارغة كالتالي:

const App = () => {
  const name = 'Peter'
  const age = 10

  return (
    <>
      <h1>Greetings</h1>
      <Hello name="Maya" age={26 + 10} />
      <Hello name={name} age={age} />
      <Footer />
    </>
  )
}

سيُترجم الآن التطبيق بنجاح، ولن تحتوي شيفرة DOM على عناصر div زائدة.

التمارين 1.1 - 1.2

تسلّم التمارين عبر GitHub، وتحدَّد التمارين التي نفذت من خلال تطبيق تسليم الملفات submission application. يمكنك وضع كل تمارين المنهاج التي سلمتها في نفس المكان على المنصة أو في عدة أماكن، لكن عليك أن تستخدم تسميات منطقية للمجلدات إن كنت ستضع تمارين كل الأقسام في نفس المكان. يمكنك اتباع المخطط التالي لتنظيم المجلدات والتمارين المسلمة:

part0
part1
  courseinfo
  unicafe
  anecdotes
part2
  phonebook
  countries

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

انتبه يضم هذا القسم تمارين أخرى غير التمارين التي سنستعرضها الآن، لا تسلم التمارين التي ستنجزها في هذا الفصل بل انتظر حتى تنهي جميع تمارين القسم.

1.1- معلومات عن المنهاج course information، الخطوة 1

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

استخدم create-react-app لإنشاء تطبيق جديد وعدل الملف index.js ليظهر كالتالي:

import React from 'react'
import ReactDOM from 'react-dom'

const App = () => {
  const course = 'Half Stack application development'
  const part1 = 'Fundamentals of React'
  const exercises1 = 10
  const part2 = 'Using props to pass data'
  const exercises2 = 7
  const part3 = 'State of a component'
  const exercises3 = 14

  return (
    <div>
      <h1>{course}</h1>
      <p>
        {part1} {exercises1}
      </p>
      <p>
        {part2} {exercises2}
      </p>
      <p>
        {part3} {exercises3}
      </p>
      <p>Number of exercises {exercises1 + exercises2 + exercises3}</p>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

احذف الملفات الزائدة وهي: App.js وApp.css وApp.test.js وlogo.svg وsetupTests.js وserviceWorker.js.

لسوء الحظ فالتطبيق ككل محتوًى في نفس المكوِّن، أعد صياغة التطبيق ليضم ثلاثة مكوِّنات جديدة: Header وContent وTotal. عدل الشيفرة التي تمرر البيانات في التطبيق App عبر الخصائص واجعل المكوِّن Header يتولى أمر البيانات المتعلقة باسم المنهاج، واجعل المكوِّن Content مسؤولًا عن تصيير البيانات المتعلقة بالأقسام وعدد التمارين في كل منها، وأخيرًا اجعل المكوِّن Total مسؤولًا عن تصيير البيانات المتعلقة بالعدد الكلي للتمارين. سيظهر جسم التطبيق الجديد تقريبًا كما يلي:

const App = () => {
  // const-definitions

  return (
    <div>
      <Header course={course} />
      <Content ... />
      <Total ... />
    </div>
  )
}

تحذير تنشئ الأداة create-react-app مستودع git محلي يحتوي المشروع ، إلا إن كان في المجلد مستودع محلي سابق. من المرجح أنك لا تريد أن يغدو المشروع مستودعًا، لهذا نفذ الأمر التالي rm -rf .git في مسار المشروع.

1.2- معلومات عن المنهاج، الخطوة 2

أعد صياغة المكوِّن Content بحيث لا يصيّر بنفسه أسماء الأقسام أو عدد التمارين التي تضمها، بل يصير ثلاثة مكوِّنات تدعى Part، ثم يُصيّر كل مكوّن منها اسم قسم واحد وعدد التمارين التي يضمها.

const Content = ... {
  return (
    <div>
      <Part .../>
      <Part .../>
      <Part .../>
    </div>
  )
}

يمرر تطبيقنا حاليًا المعلومات بطريقة بدائية لاعتماده على متغيرات مفردة. سيتحسن الأمر قريبًا.

اقترح تعديلًا على مضمون الفصل الأصلي.

ترجمة -وبتصرف- للفصل Introduction to React من سلسلة Deep Dive Into Modern Web Development



1 شخص أعجب بهذا


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


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



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن