full_stack_101 إضافة تنسيقات إلى تطبيق React


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

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

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

سننشئ ملفًا باسم index.css ضمن المجلد src ثم نضيفه إلى التطبيق باستخدام تعليمة الإدراج import:

import './index.css'

لنضف بعض قواعد التنسيق إلى الملف:

h1 {
  color: green;
}

تتألف قاعدة التنسيق من المحدد selector والتصريح declaration. يعرّف المحدد العنصر الذي تطبق عليه القاعدة. يطبق المحدد في المثال السابق على عنصر العنوان H1 في التطبيق. بينما ينص التصريح على تغيير خاصية اللون إلى الأخضر. يمكن لقاعدة واحدة من قواعد التنسيق أن تغير أي عدد من الخصائص، لنغير إذًا تنسيق الخط ليصبح مائلًا:

h1 {
  color: green;
  font-style: italic;
}

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

const Note = ({ note, toggleImportance }) => {
  const label = note.important 
    ? 'make not important' 
    : 'make important';

  return (
    <li>
      {note.content} 
      <button onClick={toggleImportance}>{label}</button>
    </li>
  )
}

لنضف القاعدة التالية إلى ملف التنسيق:

li {
  color: grey;
  padding-top: 3px;
  font-size: 15px;
}

إن اختيار قواعد التنسيق لتستهدف العناصر سيسبب بعض المشاكل. فلو احتوى تطبيقنا على عناصر li أخرى ستطبق القاعدة عليها أيضًا. ولتطبيق التنسيق على الملاحظات حصرًا، من الأفضل استخدام محددات الأصناف class selectors. تعّرف محددات الأصناف في HTML القياسية كقيمة للصفة class:

<li class="note">some text...</li>

بينما في React، علينا أن نستخدم الصفة className بدلًا من الصفة class. لنجري الآن بعض التغييرات على المكوِّن Note:

const Note = ({ note, toggleImportance }) => {
  const label = note.important 
    ? 'make not important' 
    : 'make important';

  return (
    <li className='note'>
      {note.content} 
      <button onClick={toggleImportance}>{label}</button>
    </li>
  )
}

لاحظ كيف عرفنا محدد الصنف ضمن الصفة classname:

.note {
  color: grey;
  padding-top: 5px;
  font-size: 15px;
}

لن تتأثر الآن عناصر li الأخرى الآن بهذه القاعدة بل فقط العناصر التي تظهر الملاحظات.

رسائل خطأ بمظهر أفضل

استخدمنا سابقا التابع alert لإظهار رسالة خطأ عندما يحاول المستخدم تغيير أهمية ملاحظة محذوفة. لننشئ الآن مكوِّنًا خاصًا بإظهار رسائل الخطأ:

const Notification = ({ message }) => {
  if (message === null) {
    return null
  }

  return (
    <div className="error">
      {message}
    </div>
  )
}

لن يصيّر شيء على الشاشة إن كانت رسالة الخطأ فارغة، بينما ستصيّر الرسالة في بقية الحالات داخل عنصر div. لنضف قطعة حالة تدعى errorMessage إلى المكوِّن App. ونعطها رسالة خطأ ما كقيمة ابتدائية لنتمكن من اختبارها مباشرة:

const App = () => {
  const [notes, setNotes] = useState([]) 
  const [newNote, setNewNote] = useState('')
  const [showAll, setShowAll] = useState(true)
  const [errorMessage, setErrorMessage] = useState('some error happened...')
  // ...

  return (
    <div>
      <h1>Notes</h1>
      <Notification message={errorMessage} />      <div>
        <button onClick={() => setShowAll(!showAll)}>
          show {showAll ? 'important' : 'all' }
        </button>
      </div>      
      // ...
    </div>
  )
}

لنضف قاعدة تنسيق جديدة باسم error تناسب رسالة الخطأ:

.error {
  color: red;
  background: lightgrey;
  font-size: 20px;
  border-style: solid;
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 10px;
}

سنضيف الآن آلية لظهور رسالة الخطأ، وذلك بتعديل شيفرة الدالة toggleImportanceOf على النحو التالي:

  const toggleImportanceOf = id => {
    const note = notes.find(n => n.id === id)
    const changedNote = { ...note, important: !note.important }

    noteService
      .update(changedNote).then(returnedNote => {
        setNotes(notes.map(note => note.id !== id ? note : returnedNote))
      })
      .catch(error => {
        setErrorMessage(
            `Note '${note.content}' was already removed from server`
        )
        setTimeout(() => {
            setErrorMessage(null)
        }, 5000)
        setNotes(notes.filter(n => n.id !== id))
      })
  }

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

error_message_style_001.png

يمكن الحصول على شيفرة التطبيق بوضعه الحالي في الفرع part2-7 على github.

التنسيق ضمن السياق

تعطي React الإمكانية لتنسيق العناصر مباشرة ضمن سياق الشيفرة أو ما يدعى التنسيق ضمن السياق inline styles. والغاية من ذلك أن نزوّد كل مكوِّن في React بمجموعة من التنسيقات المعرفة داخل كائن JavaScript من خلال الصفة style. تعرّف التنسيقات بشكل مختلف قليلًا في JavaScript عما هي عليه في ملف CSS. فلو أردنا أن يظهر عنصر ما باللون الأخضر وأن يكون مائلًا حجمه 16 بيكسل، سنجد أن هذه القاعدة ستكتب في CSS بالشكل:

{
  color: green;
  font-style: italic;
  font-size: 16px;
}

بينما في React كتنسيق مباشر في المكان ستكون كالتالي:

 {
  color: 'green',
  fontStyle: 'italic',
  fontSize: 16
}

لاحظ كيف عرّفت كل خاصة تنسيق بشكل مستقل ضمن كائن JavaScript، وأن القيم العددية المقدّرة بالبيكسل يمكن تعريفها ببساطة كأعداد صحيحة. أحد الاختلافات الرئيسة بين الأسلوبين هو أن كتابة الخصائص بطريقة أسياخ الشواء (kebab case) المتبعة في CSS ستتغير إلى طريقة سنام الجمل (camelCase) في JavaScript.

سنضيف أخيرًا كتلة سفلية لتطبيقنا بإنشاء مكوِّن جديد يدعى Footer وننسقه في مكانه كالتالي:

const Footer = () => {
  const footerStyle = {
    color: 'green',
    fontStyle: 'italic',
    fontSize: 16
  }
  return (
    <div style={footerStyle}>
      <br />
      <em>Note app, Department of Computer Science, University of Helsinki 2020</em>
    </div>
  )
}

const App = () => {
  // ...

  return (
    <div>
      <h1>Notes</h1>

      <Notification message={errorMessage} />

      // ...  

      <Footer />
    </div>
  )
}

يأتي التنسيق ضمن السياق بمحدودية معينة. فلا يمكن استخدام أصناف الحالات الزائفة pseudo-classes مباشرة على سبيل المثال. إنّ استخدام التنسيق ضمن السياق وبعض الطرق الأخرى في تعريف التنسيقات ضمن مكوِّنات React تتعارض مع الأعراف المتبعة سابقًا. فقد اعتبر فصل التنسيقات CSS عن المحتوى HTML وعن الوظائف JavaScript هو العمل المثالي. وكان الهدف من ذلك وفقًا لأفكار المدرسة التقليدية، هو كتابة كل منها في ملفه الخاص.

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

يمكن الحصول على شيفرة التطبيق بوضعه الحالي في الفرع part2-8 على github.

التمارين 2.19- 2.20

2.19 دليل الهاتف: الخطوة 11

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

phonebook_step11_002.png

2.20 دليل الهاتف: الخطوة 12 *

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

phonebook_step12_success_003.png

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

phonebook_step12_fail_004.png

ملاحظة: يجب أن تظهر رسالة الخطأ حتى لو تمت معالجة الخطأ.

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

ترجمة -وبتصرف- للفصل Adding styles to react app من سلسلة Deep Dive Into Modern Web Development





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


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



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

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

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


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

تسجيل الدخول

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


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