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

السؤال

نشر

السلام عليكم.

أود القيام بtimer قصد حساب وقت محدد.

المشكل الذي إعترضني هو زيادة 2ثواني في كل مرة. لم أدر لماذا.

الكود

const [timer, setTimer] = useState(0)

  const format = (time) => {
    let hours = Math.floor((time / 60 / 60) % 24);
    let minutes = Math.floor((time / 60) % 60);
    let secondes = Math.floor(time % 60);

    hours = hours < 10 ? "0" + hours : hours;
    minutes = minutes < 10 ? "0" + minutes : minutes;
    secondes = secondes < 10 ? "0" + secondes : secondes;

    return hours + ":" + minutes + ":" + secondes;
  };

  useEffect(() => {
    setInterval(() => {
      setTimer(timer => timer + 1);
    }, 1000);
  }, []);

شكرا

Recommended Posts

  • 0
نشر

يحتمل أن المشكلة في استخدام setInterval داخل useEffect، حيث أن setInterval لا يتوقف عن العمل عند إعادة رسم العنصر الخاص بالمؤشر الزمني، مما يؤدي إلى تكرار تشغيل المؤشر الزمني وزيادة ثانيتين في كل دورة.

يمكن استخدام setTimeout بدلاً من setInterval، وإعادة تشغيل المؤشر الزمني فقط عند تغيير الحالة. وللحفاظ على تحديث المؤشر الزمني كل ثانية، يمكن إنشاء دالة مساعدة تستدعى setTimeout مع مهلة زمنية وتحديث الحالة.

const [timer, setTimer] = useState(0);

const format = (time) => {
  let hours = Math.floor((time / 60 / 60) % 24);
  let minutes = Math.floor((time / 60) % 60);
  let secondes = Math.floor(time % 60);

  hours = hours < 10 ? "0" + hours : hours;
  minutes = minutes < 10 ? "0" + minutes : minutes;
  secondes = secondes < 10 ? "0" + secondes : secondes;

  return hours + ":" + minutes + ":" + secondes;
};

const updateTimer = () => {
  setTimer((timer) => timer + 1);
  setTimeout(updateTimer, 1000);
};

useEffect(() => {
  setTimeout(updateTimer, 1000);
}, []);

return <div>{format(timer)}</div>;

 

  • 0
نشر

عند استخدام الدالة setInterval ، يتم استدعاؤها بشكل متكرر كل 1000 مللي ثانية (1 ثانية) وتقوم بتحديث حالة الـ timer.

ومع ذلك، يمكن أن يحدث تأخير في عملية تحديث الحالة، وهذا يؤدي إلى تجاوز قيمة timer المرتقبة بشكل خاطئ.

لذلك باستطاعتك استخدام دالة الـ useEffect لتحديث قيمة الـ timer بشكل دقيق بعد انتهاء الـ interval.

const [timer, setTimer] = useState(0);

  const format = (time) => {
    let hours = Math.floor((time / 60 / 60) % 24);
    let minutes = Math.floor((time / 60) % 60);
    let secondes = Math.floor(time % 60);

    hours = hours < 10 ? "0" + hours : hours;
    minutes = minutes < 10 ? "0" + minutes : minutes;
    secondes = secondes < 10 ? "0" + secondes : secondes;

    return hours + ":" + minutes + ":" + secondes;
  };

  useEffect(() => {
    const interval = setInterval(() => {
      setTimer((timer) => timer + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  useEffect(() => {
    const timerValue = format(timer);

    // Do something with the timerValue, like displaying it
  }, [timer]);

من خلال إضافة دالة useEffect جديدة والتي تتم استدعائها عند تحديث الـ timer يتم إنشاء متغير يحتوي على القيمة المنسقة للـ timer باستخدام دالة format، وعليك باستخدام هذا المتغير لعرض الـ timer بالشكل المطلوب.

ولاحظ أن دالة clearInterval مفادها هو إلغاء الـ interval بشكل صحيح عندما يتم إيقاف تشغيل المؤشر، وإعادة ترتيب الكود عن طريق نقل دالة setInterval إلى داخل دالة useEffect.

وإحدى الطرق الأفضل هي استخدام دالة useRef لحفظ قيمة الـ interval والسماح لنا بإيقافها بشكل دقيق وفي الوقت المناسب، بالشكل التالي:

import { useState, useEffect, useRef } from "react";

function Timer() {
  const [timer, setTimer] = useState(0);
  const intervalRef = useRef();

  const format = (time) => {
    let hours = Math.floor((time / 60 / 60) % 24);
    let minutes = Math.floor((time / 60) % 60);
    let secondes = Math.floor(time % 60);

    hours = hours < 10 ? "0" + hours : hours;
    minutes = minutes < 10 ? "0" + minutes : minutes;
    secondes = secondes < 10 ? "0" + secondes : secondes;

    return hours + ":" + minutes + ":" + secondes;
  };

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setTimer((timer) => timer + 1);
    }, 1000);

    return () => {
      clearInterval(intervalRef.current);
    };
  }, []);

  const stopTimer = () => {
    clearInterval(intervalRef.current);
  };

  const resetTimer = () => {
    setTimer(0);
  };

  return (
    <div>
      <div>{format(timer)}</div>
      <button onClick={stopTimer}>Stop Timer</button>
      <button onClick={resetTimer}>Reset Timer</button>
    </div>
  );
}

تستخدم دالة useRef لإنشاء مرجع ref جديد لـ interval وحفظ قيمته في متغير، وإرجاع الدالة useEffect التي تستخدم intervalRef.current بدلاً من interval وإعادة تعيين قيمة intervalRef.current عندما يتم تحديث الـ timer.

كما تم إضافة دالة stopTimer لإلغاء الـ interval و إنشاء دالة resetTimer لإعادة الـ timer إلى القيمة الافتراضية.

وإضافة زر "Stop Timer" لإيقاف تشغيل المؤشر وزر "Reset Timer" لإعادة تعيين الـ timer إلى القيمة الافتراضية، وتستطيع استخدام الـ format() في عرض الـ timer بالشكل المطلوب.

 

  • 0
نشر

يبدو أن المشكلة هي في استخدام الدالة setInterval مع الدالة setTimer. تحتوي الدالة setInterval على تأخير في تنفيذ الكود الذي يتم تمريره لها، والذي قد يؤدي إلى تغيير قيمة timer عدة مرات بدلاً من مرة واحدة فقط.

يمكن تجنب هذه المشكلة عن طريق استخدام دالة العد التنازلي setTimeout بدلاً من setInterval. يمكنك استخدام الكود التالي كبديل:

const [timer, setTimer] = useState(0)

  const format = (time) => {
    let hours = Math.floor((time / 60 / 60) % 24);
    let minutes = Math.floor((time / 60) % 60);
    let secondes = Math.floor(time % 60);

    hours = hours < 10 ? "0" + hours : hours;
    minutes = minutes < 10 ? "0" + minutes : minutes;
    secondes = secondes < 10 ? "0" + secondes : secondes;

    return hours + ":" + minutes + ":" + secondes;
  };

  const incrementTimer = () => {
    setTimer(timer + 1);
    setTimeout(incrementTimer, 2000); // تمديد الوقت بـ 2 ثواني
  };

  useEffect(() => {
    setTimeout(incrementTimer, 2000);
  }, []);

يقوم الكود الجديد بإنشاء دالة incrementTimer تقوم بزيادة الوقت بمقدار ثانية واحدة ، ثم تستدعي نفسها باستخدام setTimeout بتأخير يبلغ 2 ثوانٍ. يتم استدعاء incrementTimer مرة واحدة فقط في useEffect بمجرد تحميل الصفحة.

وبالتالي، سيتم زيادة الوقت بمقدار ثانية واحدة كل ثانيتين بدلاً من كل ثانية واحدة كما كان في الكود الأصلي.

  • 0
نشر

بالإضافة إلى استخدام `setTimeout` ، هناك طرق أخرى لتحديث الحالة بشكل دوري. من بين هذه الطرق:

  •  استخدام `setInterval` مع تحديث القيمة بشكل صحيح:

يمكن استخدام `setInterval` لتحديث الحالة بشكل دوري ، ولكن يجب التأكد من تحديث القيمة بشكل صحيح من خلال إرجاع دالة من `useEffect` تستخدم لإلغاء `setInterval` عندما يتم إعادة تقديم المكون أو إزالته. يمكن استخدام الكود التالي لتحقيق ذلك:

useEffect(() => {
  const intervalId = setInterval(() => {
    setTimer(timer => timer + 1);
  }, 1000);

  return () => clearInterval(intervalId);
}, []);
  • استخدام `requestAnimationFrame`:

يمكن استخدام `requestAnimationFrame` لتحديث الحالة بشكل دوري ، ولكن يجب التأكد من تحديث القيمة بشكل صحيح من خلال إرجاع دالة من `useEffect` تستخدم لإلغاء `requestAnimationFrame` عندما يتم إعادة تقديم المكون أو إزالته. يمكن استخدام الكود التالي لتحقيق ذلك:

const requestRef = useRef();
const previousTimeRef = useRef();

const animate = (time) => {
  if (previousTimeRef.current != undefined) {
    const deltaTime = time - previousTimeRef.current;
    setTimer(timer => timer + deltaTime / 1000);
  }
  previousTimeRef.current = time;
  requestRef.current = requestAnimationFrame(animate);
}

useEffect(() => {
  requestRef.current = requestAnimationFrame(animate);
  return () => cancelAnimationFrame(requestRef.current);
}, []);
  •  استخدام مكتبات التحديث الدوري:

هناك العديد من مكتبات التحديث الدوري المتوفرة لـ React مثل `react-timer-hook` و `react-use` و `useInterval` والتي تسمح بتحديث الحالة بشكل دوري بسهولة. يمكن استخدام هذه المكتبات لتحقيق الهدف بشكل أسهل وأكثر فعالية.

توجد العديد من الطرق لتحديث الحالة بشكل دوري في React ، والطريقة الأفضل تعتمد على متطلبات التطبيق والحالة التي تحتاج إلى التحديث.

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

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

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.

  • إعلانات

  • تابعنا على



×
×
  • أضف...