في هذا المنشور، سأوضح كيفية إنشاء إغلاق في تطبيق React لربط useState.
لن أشرح ما هو الإغلاق، لأن هناك العديد من الموارد حول هذا الموضوع ولا أريد أن أكرر. أنصح بقراءة هذا المقال بقلم @imranabdulmalik.
باختصار، الإغلاق هو (من موزيلا):
...مجموعة دالة مجمعة معًا (مرفقة) مع إشارات إلى الحالة المحيطة بها (البيئة المعجمية). بمعنى آخر، يمنحك الإغلاق إمكانية الوصول إلى نطاق الوظيفة الخارجية من وظيفة داخلية. في JavaScript، يتم إنشاء عمليات الإغلاق في كل مرة يتم فيها إنشاء دالة، في وقت إنشاء الوظيفة.
فقط في حالة عدم معرفتك بمصطلح البيئة المعجمية، يمكنك قراءة هذه المقالة بواسطة @soumyadey أو بدلاً من ذلك هذه المقالة.
في تطبيق React، يمكنك عن طريق الخطأ إنشاء إغلاق لمتغير ينتمي إلى حالة المكون التي تم إنشاؤها باستخدام الخطاف useState. عندما يحدث هذا، فإنك تواجه مشكلة الإغلاق القديم، أي عندما تشير إلى قيمة قديمة للحالة التي تغيرت في هذه الأثناء، وبالتالي فهي ليست أكثر صلة.
لقد قمت بإنشاء تطبيق Demo React والذي يتمثل هدفه الرئيسي في زيادة عداد (ينتمي إلى الحالة) يمكن إغلاقه في عملية إغلاق في رد الاتصال لطريقة setTimeout.
باختصار، يمكن لهذا التطبيق:
في الصورة التالية، تظهر حالة واجهة المستخدم الأولية للتطبيق، مع عداد للصفر.
سنقوم بمحاكاة إغلاق العداد في ثلاث خطوات:
بعد 5 ثواني تصبح قيمة العداد 2.
القيمة المتوقعة للعداد يجب أن تكون 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 (); } export default App;Closure demo
Counter value: {counter}
Follow the istructions to create a closure of the state variable counter
- Set the counter to preferred value
- Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
- Increment by 10 the counter before the timeout
{ renderLogs() }
يعتمد الحل على استخدام خطاف 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)
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3