"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > رد: إغلاق قديم

رد: إغلاق قديم

تم النشر بتاريخ 2024-09-02
تصفح:208

في هذا المنشور، سأوضح كيفية إنشاء إغلاق في تطبيق React لربط useState.

لن أشرح ما هو الإغلاق، لأن هناك العديد من الموارد حول هذا الموضوع ولا أريد أن أكرر. أنصح بقراءة هذا المقال بقلم @imranabdulmalik.

باختصار، الإغلاق هو (من موزيلا):

...مجموعة دالة مجمعة معًا (مرفقة) مع إشارات إلى الحالة المحيطة بها (البيئة المعجمية). بمعنى آخر، يمنحك الإغلاق إمكانية الوصول إلى نطاق الوظيفة الخارجية من وظيفة داخلية. في JavaScript، يتم إنشاء عمليات الإغلاق في كل مرة يتم فيها إنشاء دالة، في وقت إنشاء الوظيفة.

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

المشكلة

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

نقطة الاتصال

لقد قمت بإنشاء تطبيق Demo React والذي يتمثل هدفه الرئيسي في زيادة عداد (ينتمي إلى الحالة) يمكن إغلاقه في عملية إغلاق في رد الاتصال لطريقة setTimeout.

باختصار، يمكن لهذا التطبيق:

  • إظهار قيمة العداد
  • الزيادة بمقدار 1 عداد
  • ابدأ مؤقتًا لزيادة العداد بمقدار 1 بعد خمس ثوانٍ.
  • زيادة بمقدار 10 عداد

في الصورة التالية، تظهر حالة واجهة المستخدم الأولية للتطبيق، مع عداد للصفر.

React: stale closure

سنقوم بمحاكاة إغلاق العداد في ثلاث خطوات:

  1. الزيادة بمقدار 1 عداد

React: stale closure

  1. بدء تشغيل المؤقت لزيادة بمقدار 1 بعد خمس ثوان

React: stale closure

  • زيادة بمقدار 10 قبل بدء المهلة React: stale closure

بعد 5 ثواني تصبح قيمة العداد 2.

React: stale closure

القيمة المتوقعة للعداد يجب أن تكون 12، لكننا نحصل على 2.

سبب حدوث ذلك هو أننا أنشأنا إغلاق العداد في رد الاتصال الذي تم تمريره إلى setTimeout وعندما يتم تشغيل المهلة نقوم بتعيين العداد بدءًا من وقته القيمة القديمة (التي كانت 1).

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);

متابعة الكود الكامل لمكون التطبيق.

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    حل

    يعتمد الحل على استخدام خطاف useRef الذي يتيح لك الإشارة إلى قيمة غير مطلوبة للعرض.

    لذلك نضيف إلى مكون التطبيق:

    const currentCounter = useRef(counter)
    

    ثم سنقوم بتعديل رد الاتصال الخاص بـ setTimeout كما هو موضح أدناه:

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current   1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    

    يحتاج رد الاتصال الخاص بنا إلى قراءة قيمة العداد لأننا نسجل القيمة الحالية قبل زيادتها.

    في حالة عدم الحاجة إلى قراءة القيمة، يمكنك تجنب إغلاق العداد فقط باستخدام التدوين الوظيفي لتحديث العداد.

    seCounter(c => c   1)
    

    موارد

    • ديمتري بافلوتين كن على دراية بعمليات الإغلاق القديمة عند استخدام خطافات React
    • عمران عبد الملك إتقان عمليات الإغلاق في جافا سكريبت: دليل شامل
    • النطاق المعجمي Keyur Paralkar في JavaScript – دليل المبتدئين
    • عمليات الإغلاق التي لا معنى لها لسوفيك بول في رد الفعل
    • سمية داي فهم النطاق المعجمي والإغلاقات في جافا سكريبت
    • سوباش ماهاباترا ستاكوفرفلو
    بيان الافراج تم نشر هذه المقالة على: https://dev.to/animusna/react-stale-closure-81a?1 إذا كان هناك أي انتهاك، يرجى الاتصال بـ [email protected] لحذفه
    أحدث البرنامج التعليمي أكثر>

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

    Copyright© 2022 湘ICP备2022001581号-3