دليل شامل: بناء تطبيق للتحكم في درجة الحرارة باستخدام React مع نصائح وكود جاهز

دقائق القراءة: 12
مرحباً بك أيها المطور الطموح في منصة قيد! في هذا الدليل الشامل، سنخوض غمار بناء تطبيق تفاعلي للتحكم في درجة الحرارة باستخدام مكتبة React الشهيرة. هذا المشروع الموجه للمبتدئين سيزودك بالمعرفة والمهارات الأساسية اللازمة لفهم آليات React، بما في ذلك استخدام هوكات الحالة (State Hooks)، وكيفية التعامل مع الأحداث (Event Handling)، وتطبيق أنماط CSS بناءً على حالة المكونات، والمزيد. استعد لرحلة شيقة نحو بناء تطبيق عملي ومفيد!

ماذا سنبني في هذا المشروع؟

نظرة عامة على التطبيق النهائي

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

تحدي التطوير: حاول بنفسك أولاً!

متطلبات وسيناريوهات التطبيق

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

  • عندما ينقر المستخدم على زر “الزيادة” (increase button)، يجب أن تزداد درجة الحرارة.
  • يجب ألا تتجاوز درجة الحرارة 30 درجة مئوية.
  • عندما ينقر المستخدم على زر “الخفض” (decrease button)، يجب أن تنخفض درجة الحرارة.
  • يجب ألا تقل درجة الحرارة عن 0 درجة مئوية.
  • عندما تكون درجة الحرارة 15 درجة مئوية أو أعلى، يجب أن يتغير لون الخلفية إلى الأحمر (تلميح: لقد قمنا بتضمين نمط CSS يسمى "hot" يمكنك استخدامه).
  • عندما تكون درجة الحرارة أقل من 15 درجة مئوية، يجب أن يتغير لون الخلفية إلى الأزرق (تلميح: لقد قمنا بتضمين نمط CSS يسمى "cold" يمكنك استخدامه).

إعداد بيئة العمل وكود البدء

تهيئة مشروع React جديد

نفترض أن لديك بيئة تطوير React مُعدة مسبقاً على جهازك. إذا لم يكن الأمر كذلك، يمكنك مراجعة المصادر التعليمية المتاحة لمساعدتك في البدء. كل ما نحتاجه للبدء هو استخدام أداة create-react-app. افتح الطرفية (terminal) وقم بتشغيل الأمر التالي:

npx create-react-app temperature-control

دع الطرفية تكمل عملها، ثم افتح المشروع في محرر الأكواد VS Code (أو أي محرر تستخدمه). بعد ذلك، انتقل إلى الملف index.js، احذف كل محتوياته، والصق الكود التالي:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

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

إضافة الأنماط الأساسية (CSS)

الآن، انتقل إلى الملف index.css، احذف كل محتوياته، والصق الأنماط التالية:

body {
  font-family: sans-serif;
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  min-height: 100vh;
}

.app-container {
  height: 400px;
  width: 300px;
  background: #2b5870;
  border-radius: 20px;
  box-shadow: 10px 10px 38px 0px rgba(0, 0, 0, 0.75);
}

.temperature-display-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 70%;
}

.temperature-display {
  display: flex;
  border-radius: 50%;
  color: #ffffff;
  height: 220px;
  width: 220px;
  text-align: center;
  justify-content: center;
  align-items: center;
  font-size: 48px;
  border: 3px #ffffff solid;
  transition: background 0.5s;
}

button {
  border-radius: 100px;
  height: 80px;
  width: 80px;
  font-size: 32px;
  color: #ffffff;
  background: rgb(105, 104, 104);
  border: 2px #ffffff solid;
}

button:hover {
  background: rgb(184, 184, 184);
  cursor: pointer;
}

button:focus {
  outline: 0;
}

.button-container {
  display: flex;
  justify-content: space-evenly;
  align-items: center;
}

.neutral {
  background: rgb(184, 184, 184);
}

.cold {
  background: #035aa6;
}

.hot {
  background: #ff5200;
}

بناء المكون الأساسي (App.js)

أخيراً، انتقل إلى الملف App.js، احذف كل محتوياته، والصق الكود التالي:

import React from 'react';

const App = () => {
  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className='temperature-display'>10°C</div>
      </div>
      <div className='button-container'>
        <button>+</button>
        <button>-</button>
      </div>
    </div>
  );
};

export default App;

تشغيل التطبيق

الآن يمكننا فتح طرفية جديدة داخل VS Code وتشغيل الأمر التالي:

npm start

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

جعل قيمة درجة الحرارة ديناميكية باستخدام هوك الحالة (useState)

فهم مفهوم الحالة (State) في React

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

استيراد واستخدام هوك useState

في ملف App.js، قم باستيراد هوك useState في أعلى الملف كالتالي:

import React, { useState } from 'react';

ثم، أضف السطر التالي داخل دالة المكون App:

const [temperatureValue, setTemperatureValue] = useState(10);

شرح موجز لهوك useState

تذكير سريع بهوك useState: يسمح لنا هذا الهوك بالاحتفاظ بالبيانات في حالة المكون. يوفر لنا useState شيئين رئيسيين:

  • متغير يحمل قيمة الحالة الحالية (في حالتنا: temperatureValue).
  • دالة لتغيير قيمة الحالة (في حالتنا: setTemperatureValue).

لقد قمنا بتهيئة قيمة temperatureValue لتكون 10، وذلك بتمرير القيمة 10 إلى هوك useState.

ربط الحالة بواجهة المستخدم (JSX)

الآن بعد أن أصبح لدينا قيمة حالة، حان الوقت لاستخدامها في الكود الخاص بنا. تذكر أن القيم التي نحصل عليها من useState يمكن استخدامها تماماً مثل أي متغير أو دالة JavaScript عادية. داخل JSX الخاص بنا، نريد استبدال قيمة درجة الحرارة الثابتة بمتغير الحالة الجديد الخاص بنا. قم بتغيير هذا السطر:

<div className='temperature-display'> 10 °C</div>

ليصبح كالتالي:

<div className='temperature-display'>{temperatureValue}°C</div>

لاحظ كيف استخدمنا الأقواس المعقوفة {} لعرض متغير temperatureValue. الآن، عندما تتغير قيمة درجة الحرارة، سيتم إعادة عرض المكون وعرض قيمة درجة الحرارة الجديدة. ملف App.js الخاص بنا يبدو حتى الآن كالتالي:

import React, { useState } from 'react';

const App = () => {
  const [temperatureValue, setTemperatureValue] = useState(10);

  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className='temperature-display'> {temperatureValue}°C </div>
      </div>
      <div className='button-container'>
        <button> + </button>
        <button> - </button>
      </div>
    </div>
  );
};

export default App;

إذا قمت بتشغيل التطبيق الآن، ونظرت إلى المتصفح، ستلاحظ أن الأمور تبدو كما كانت من قبل. ولكن إذا قمت بتغيير القيمة الأولية التي نمررها إلى هوك useState من 10 إلى شيء آخر (على سبيل المثال 15)، فسترى أن التطبيق يتحدث. هذا يعني أن هوك الحالة لدينا يعمل!

تغيير الحالة عند النقر على الأزرار

ربط دوال تغيير الحالة بأحداث النقر

دعنا نعمل على جعل قيمة درجة الحرارة تزداد/تنقص عند النقر على الأزرار. كما نعلم، يوفر لنا هوك useState دالة setTemperatureValue التي يمكننا استخدامها لتغيير قيمة temperatureValue. لذلك، من المنطقي ربط هذه الدالة بحدث onClick الخاص بالزر. سنقوم أولاً بإنشاء زر الزيادة. استبدل زر الزيادة بالكود التالي:

<button onClick={ () => setTemperatureValue(temperatureValue + 1 )}>+</button>

لاحظ كيف يستدعي هذا الكود دالة setTemperatureValue. نحن نأخذ قيمة temperatureValue الحالية، نضيف إليها 1، ونمرر هذه القيمة كوسيط. لذا، بما أن temperatureValue تبدأ من 10، فإن إضافة 1 ستجعل قيمة الحالة 11. عند النقر على الزر مرة أخرى، يتم تعيين الحالة إلى 12، وهكذا.

بعد ذلك، سنفعل الشيء نفسه مع زر الخفض. استبدل زر الخفض الحالي بالكود التالي:

<button onClick={ () => setTemperatureValue(temperatureValue - 1 )}>-</button>

هذا يقوم بنفس الشيء، باستثناء أننا نقوم بخفض temperatureValue هذه المرة. يبدو الكود الخاص بنا الآن كالتالي:

import React, { useState } from 'react';

const App = () => {
  const [temperatureValue, setTemperatureValue] = useState(10);

  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className='temperature-display'> {temperatureValue}°C </div>
      </div>
      <div className='button-container'>
        <button onClick = {() => setTemperatureValue(temperatureValue + 1)} >+ </button>
        <button onClick = {() => setTemperatureValue(temperatureValue - 1)} >- </button>
      </div>
    </div>
  );
};

export default App;

جرب تشغيل هذا في المتصفح وانقر على الأزرار. ستزداد القيم/تنقص بشكل صحيح.

تغيير لون العرض بناءً على حالة درجة الحرارة

تطبيق الأنماط الشرطية (Conditional Styling)

الآن دعنا نقوم ببعض الأشياء المتقدمة. نريد أن يتغير لون خلفية شاشة العرض بناءً على مدى ارتفاع (أو انخفاض) درجة الحرارة. إذا كانت درجة الحرارة 15 درجة مئوية أو أعلى، نريد تغيير لون الخلفية إلى الأحمر. إذا كانت أقل من 15 درجة، نريد تغيير لون الخلفية إلى الأزرق.

إذا نظرت إلى ملف CSS، فقد قمنا بتوفير فئتين (classes):

  • .cold: التي تجعل الخلفية زرقاء.
  • .hot: التي تجعل الخلفية حمراء.

إذا أضفنا أياً من هاتين الفئتين إلى عنصر div الخاص بشاشة عرض درجة الحرارة، فسيتغير اللون. على سبيل المثال:

<div className= 'temperature-display cold' >{temperatureValue}°C</div>

سيعطي شاشة عرض درجة الحرارة خلفية زرقاء، بينما:

<div className= 'temperature-display hot' >{temperatureValue}°C</div>

سيعطيها خلفية حمراء.

إدارة فئة CSS ديناميكياً باستخدام الحالة

حسناً، هذا جميل، ولكن كيف نضيف هذه الفئات ديناميكياً بناءً على الحالة؟ تذكر، من الجيد عموماً وضع الأشياء التي يمكن أن تتغير في واجهة المستخدم في الحالة. لذا، فإن الحالة هي المكان المثالي للاحتفاظ بفئة CSS الحالية التي نريد استخدامها. دعنا ننشئ هوك حالة آخر للاحتفاظ بـ temperatureColor كالتالي:

const [temperatureColor, setTemperatureColor] = useState('cold');

لاحظ أننا قمنا بتهيئة كائن حالة temperatureColor بقيمة "cold" (لأن قيمة درجة الحرارة الأولية لدينا هي 10 درجات، ونريد أن يكون لون الخلفية أزرق). يمكننا بعد ذلك استخدام template literals لإضافة الفئات التي نريدها ديناميكياً باستخدام متغير الحالة هذا. قم بتحديث الكود كالتالي:

<div className={ `temperature-display ${temperatureColor} ` }>{temperatureValue}°C</div>

قد يكون هذا التركيب صعب الفهم في البداية، فلا تقلق إذا لم تفهمه على الفور. كل ما يفعله هذا هو إنشاء سلسلة نصية وتطبيق متغير temperatureColor ديناميكياً. كلما تغير temperatureColor إلى "hot"، سيتم إعادة عرض المكون وستتم إضافة فئة CSS "hot" إلى سلسلة className بدلاً من ذلك. يبدو الكود الخاص بنا حتى الآن كالتالي:

import React, { useState } from 'react';

const App = () => {
  const [temperatureValue, setTemperatureValue] = useState(10);
  const [temperatureColor, setTemperatureColor] = useState('cold');

  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className={ ` temperature-display ${ temperatureColor }`} > {temperatureValue}°C </div>
      </div>
      <div className='button-container'>
        <button onClick = {() => setTemperatureValue(temperatureValue + 1)} >+ </button>
        <button onClick = {() => setTemperatureValue(temperatureValue - 1)} >- </button>
      </div>
    </div>
  );
};

export default App;

قم بتغيير متغير حالة temperatureColor الأولي إلى "hot" / "cold" ويجب أن تتغير خلفية شاشة عرض درجة الحرارة. الآن بعد أن عرفنا أن هذا يعمل، كل ما علينا فعله هو تغيير متغير الحالة.

تحسين معالجات الأحداث (Refactoring Event Handlers)

ولكن أين نفعل هذا؟ حسناً، لدينا بالفعل معالج حدث onClick يغير قيمة temperatureValue، لذلك من المنطقي إضافة منطقنا الجديد إلى هذا المعالج. حتى الآن، كنا نستخدم دالة مضمنة (inline function) لمعالجات أحداث النقر. واستخدام الدوال المضمنة جيد عندما تكون لدينا دالة من سطر واحد. ولكن عندما تكون لدينا دالة متعددة الأسطر مع الكثير من المنطق، فمن الأفضل نقل الدالة خارج JSX. هذا يجعل الكود الخاص بنا أنظف قليلاً. قم بلصق ما يلي أسفل جميع تعريفات الحالة مباشرة:

const increaseTemperature = () => {
  setTemperatureValue(temperatureValue + 1 );
};

const decreaseTemperature = () => {
  setTemperatureValue(temperatureValue - 1 );
};

هنا نقوم بتعريف دالتين – واحدة تزيد درجة الحرارة والأخرى تخفضها. بعد ذلك، نريد تغيير خاصيتي onClick لأزرارنا لاستدعاء هذه الدوال بدلاً من الدوال المضمنة التي كانت لدينا سابقاً:

<button onClick={increaseTemperature}>+</button>
<button onClick = {decreaseTemperature} > - </ button >

الآن، بدلاً من استخدام دالة مضمنة، نقوم بتمرير مرجع إلى دالتي increaseTemperature و decreaseTemperature. يبدو الكود الخاص بنا حتى الآن كالتالي:

import React, { useState } from 'react';

const App = () => {
  const [temperatureValue, setTemperatureValue] = useState(10);
  const [temperatureColor, setTemperatureColor] = useState('cold');

  const increaseTemperature = () => {
    setTemperatureValue(temperatureValue + 1 );
  };

  const decreaseTemperature = () => {
    setTemperatureValue(temperatureValue - 1 );
  };

  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className={ ` temperature-display ${ temperatureColor }`} > {temperatureValue}°C </ div >
      </div>
      <div className='button-container'>
        <button onClick = {increaseTemperature} > + </ button >
        <button onClick = {decreaseTemperature} > - </ button >
      </div>
    </div>
  );
};

export default App;

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

تطبيق منطق تغيير اللون ضمن الدوال

حسناً! بعد الانتهاء من متعة إعادة الهيكلة، دعنا نعود إلى العمل. لقد قلنا أنه عندما تكون درجة الحرارة 15 درجة مئوية أو أعلى، نريد تغيير قيمة حالة temperatureColor. يمكننا إضافة هذا المنطق إلى دالة increaseTemperature كالتالي:

const increaseTemperature = () => {
  const newTemperature = temperatureValue + 1 ;
  setTemperatureValue(newTemperature);
  if (newTemperature >= 15 ) {
    setTemperatureColor('hot');
  }
};

ماذا فعلنا؟

  • أنشأنا متغيراً للاحتفاظ بقيمة newTemperature (فعلنا ذلك لأننا سنستخدم هذا المتغير في عدة أماكن).
  • قمنا بتعيين temperatureValue، كما فعلنا من قبل.
  • كتبنا عبارة if للتحقق مما إذا كانت قيمة newTemperature أكبر من أو تساوي 15.
  • إذا كانت الإجابة نعم، فإننا نستخدم دالة setTemperatureColor لتعيين قيمة حالة temperatureColor لتكون "hot".

لذا، كلما نقرنا على الزر عدداً كافياً من المرات بحيث تكون قيمة temperatureValue أكبر من أو تساوي 15، يتغير متغير temperatureColor، ويتم إعادة عرض المكون، وتتم إضافة فئة "hot" إلى شاشة عرض درجة الحرارة.

ولكن انتظر! لم نتعامل مع النقص بعد. وهو مشابه بشكل أساسي لدالة الزيادة:

const decreaseTemperature = () => {
  const newTemperature = temperatureValue - 1 ;
  setTemperatureValue(newTemperature);
  if (newTemperature < 15 ) {
    setTemperatureColor('cold');
  }
};

هذه المرة نطرح واحداً ونتحقق مما إذا كانت القيمة الجديدة أقل من 15 قبل تغيير لون درجة الحرارة. يبدو كود التطبيق النهائي لدينا كالتالي:

import React, { useState } = 'react';

const App = () => {
  const [temperatureValue, setTemperatureValue] = useState(10);
  const [temperatureColor, setTemperatureColor] = useState('cold');

  const increaseTemperature = () => {
    const newTemperature = temperatureValue + 1 ;
    setTemperatureValue(newTemperature);
    if (newTemperature >= 15 ) {
      setTemperatureColor('hot');
    }
  };

  const decreaseTemperature = () => {
    const newTemperature = temperatureValue - 1 ;
    setTemperatureValue(newTemperature);
    if (newTemperature < 15 ) {
      setTemperatureColor('cold');
    }
  };

  return (
    <div className='app-container'>
      <div className='temperature-display-container'>
        <div className={ ` temperature-display ${ temperatureColor }`} > {temperatureValue}°C </ div >
      </div>
      <div className='button-container'>
        <button onClick = {increaseTemperature} > + </ button >
        <button onClick = {decreaseTemperature} > - </ button >
      </div>
    </div>
  );
};

export default App;

قم بتشغيل التطبيق ويجب أن يعمل كل شيء بشكل صحيح - تهانينا!

تحدي إضافي: حدود درجة الحرارة

تطبيق قيود على قيم درجة الحرارة

ربما لاحظت أن تطبيق التحكم في درجة الحرارة الخاص بنا ليس آمناً جداً - يمكن للمستخدم زيادة درجة الحرارة حتى تصل إلى 100 درجة مئوية، أو خفضها حتى تصل إلى -100 درجة مئوية. التحدي، إذا اخترت قبوله، هو منع قيمة درجة الحرارة من تجاوز 30 درجة مئوية، ومنعها من الانخفاض عن 0 درجة مئوية. تلميح: دالتا increaseTemperature و decreaseTemperature هما المكانان المثاليان لإضافة هذا المنطق!

الخلاصة التقنية

لقد قمنا في هذا الدليل ببناء تطبيق تحكم في درجة الحرارة باستخدام React، وهو ما يمثل نقطة انطلاق ممتازة لفهم أساسيات تطوير واجهات المستخدم التفاعلية. من خلال استخدام هوك useState، تمكنا من إدارة حالة التطبيق بفعالية، مما سمح لنا بتحديث الواجهة ديناميكياً بناءً على تفاعلات المستخدم. كما تعلمنا كيفية ربط الأحداث (events) بالدوال، وتطبيق أنماط CSS بشكل شرطي لتوفير تجربة مستخدم غنية. هذا المشروع يؤكد على قوة React في بناء مكونات قابلة لإعادة الاستخدام وإدارة تدفق البيانات بشكل واضح ومنظم، مما يضع الأساس لمشاريع أكثر تعقيداً في المستقبل.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *