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

السؤال

نشر

كيف يمكنني حفظ البيانات في تخزين المحلي 

لقد حاولت كثيراً ولم يعمل معي

import React,{useState,useRef,useEffect} from "react";
import './App.css';

function App(){


  const [todos , setTodos] = useState([]);
  const inpRef = useRef()
  const handelButtonClick = () => {
  const text = inpRef.current.value;
  const newTodos = {complelted: false ,text}
    setTodos([...todos, newTodos])
    inpRef.current.value = '';
  }
  const handelItemClick = (i) => {
    const newItem = [...todos];
    newItem[i].complelted = !newItem[i].complelted;
    setTodos(newItem)
  }

  const handelDeleteItem = (i) => {
  const  newDelete = [...todos];
  newDelete.splice(i,1)
  setTodos(newDelete)
  } 
  return(
    <>
    <div className="App">
      <h2>To Do List</h2>
      <ul>
      {todos.map(({text,complelted},i) => {
        return <div className="item"><li className={complelted ? "done" : ""} onClick={() => handelItemClick(i)}>{text}</li><span onClick={() => handelDeleteItem(i)}></span></div>
      })}
      </ul>
      <input ref={inpRef} placeholder="Enter item..."/>
      <button onClick={handelButtonClick}>Add</button>
    </div>
    </>
  )
}

export default App;

 

Recommended Posts

  • 0
نشر

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

لكي يتم حفظ البيانات في التخزين المحلي (localStorage) وتستمر بعد ما يتم إعادة تحميل الصفحة، لابد أن نستخدم localStorage مع useEffect فالمطلوب كالأتي 

  • عند تشغيل التطبيق لأول مرة: نقرأ المهام من localStorage.
  • عند تغيير المهام (todos): نقوم بتحديث localStorage ونضيف المهام المحدثة به.

لهذا الأمر عليك بإضافة التالي إلى الكود الخاص بك

 // قراءة البيانات من التخزين المحلي عند تحميل الصفحة أول مرة
  useEffect(() => {
    const storedTodos = localStorage.getItem("todos");
    if (storedTodos) {
      setTodos(JSON.parse(storedTodos));
    }
  }, []);

  // حفظ المهام في التخزين المحلي عند كل تغيير في القائمة
  useEffect(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
  }, [todos]);

في الـ useEffect الأولي يتم التالي

  • هذا الكود بداخل useEffect يعمل مرة واحدة فقط عند تحميل المكون APP بسبب [] في النهاية.
  • يقوم بالبحث في localStorage عن شيء محفوظ باسم المفتاح todos.
  • إذا وجده نحوله من نص إلى مصفوفة باستخدام ()JSON.parse ثم نخزنها داخل todos باستخدام ()setTodos.

في الـ useEffect الثانية يتم التالي

  • هذا الكود بداخل useEffect يعمل في كل مرة تتغير فيها المهام todos بسبب [todos].
  • يقوم بتحويل المهام إلى نص باستخدام ()JSON.stringify ثم يقوم بحفظ هذا النص داخل localStorage باسم المفتاح todos.
  • 0
نشر

خطأ غريب حيث قمت باختبار الكود عدة مرات سواء بإعادة التحميل للتطبيق أو الإضافة أو الحذف ولا يظهر هذا الخطأ لدي.

أرجو أيضا تفريغ الـ localStorage من البيانات الموجودة به ربما هناك بيانات قديمة مخزنة به تحت المفتاح todos أيضا.

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

وأرجو أيضا استبدال هذا السطر 

return <div className="item"><li className={complelted ? "done" : ""} onClick={() => handelItemClick(i)}>{text}</li><span onClick={() => handelDeleteItem(i)}></span></div>

بالتالي

  return (
     <div className="item" key={i}>
          <li className={complelted ? "done" : ""}
              onClick={() => handelItemClick(i)}
          >
            {text}
          </li>
         <span onClick={() => handelDeleteItem(i)}></span>
     </div>
);

قمنا بإضافة الـ key للـ div وهو مهم عند العمل مع map في react.

من المفترض ألا تظهر معك أي أخطاء في الـ console أو تحذيرات ويعمل معك  التطبيق بدون مشكلة.

  • 0
نشر

هذا الخطأ يخبرك أنك تقوم بمحاول الوصول للثابت todos في سطر 15 وهذا الثابت قبل ما يتم تعريفه أو تهيئته (initialization).
لذلك يرجى نقل عملية استخدام الـ todos ومحاولة الوصول لقيمته أسفل عملية التعريف له.

فمثلا في هذا الكود

const handelButtonClick = () => {
  const text = inpRef.current.value;
  const newTodos = {complelted: false ,text}
    setTodos([...todos, newTodos])
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    inpRef.current.value = '';
  }
const [todos , setTodos] = useState([]);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

هذا الكود ليس بالظبط في أكوادك التي أرسلتها سابقا فقط لبيان كيف يمكن أن يحدث هذا الخطأ
سيظهر معك هذا الخطأ

Cannot access 'todos' before initialization

لأنك بتحاول تستخدم حاجة لسه ما اتعرفتش.

وتصحيح هذا الكود يكون كالأتي

const [todos , setTodos] = useState([]);

const handelButtonClick = () => {
  const text = inpRef.current.value;
  const newTodos = {complelted: false ,text}
    setTodos([...todos, newTodos])
    inpRef.current.value = '';
  }

لذلك قم بالبحث عن السطر الذي قم فيه بتعريف todos وانقله قبل السطر رقم 15 والتجربة مرة أخرى.

  • 0
نشر

لقد رفعت الخطافات الى الاعلى وظهر اخطاء اكثر

 import React,{useState,useRef, useEffect} from "react";
 import './App.css'
 import logo from './assets/react.svg'
function App(){
    const [todos , setTodos] = useState([]);
    const inpRef = useRef();
 /// قراءة البيانات من التخزين المحلي عند تحميل الصفحة أول مرة
  useEffect(() => {
    const storedTodos = localStorage.getItem("todos");
    if (storedTodos) {
      setTodos(JSON.parse(storedTodos));
    }
  }, []);

  // حفظ المهام في التخزين المحلي عند كل تغيير في القائمة
  useEffect(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
  }, [todos]); 

    const handelButtonClick = () => {
        const text = inpRef.current.value;
        const newTodos = {compiled: false, text}
        inpRef.current.value = '';
        setTodos([...todos,newTodos])
    }

    const handelItemClick = (i) => {
        const newItem = [...todos];
        newItem[i].compiled = !newItem[i].compiled;
        setTodos(newItem)
    }

    const handelDeleteClick = (i) => {
        const newDelete = [...todos];
        newDelete.splice(i,1)
        setTodos(newDelete);
    }
    return(
        <>
        <img src={logo} alt="" />
        <div className="App">
            <h2>To Do List</h2>
            <ul>
            {todos.map(({text,compiled}, i) => {
                return <div className="item"><li key={i} className={compiled ? 'done' : ''}  onClick={() => handelItemClick(i)}>{text}</li><span onClick={() => handelDeleteClick(i)}></span></div>
            })}
            </ul>
            <input ref={inpRef} placeholder="Enter item..."/>
            <button  onClick={handelButtonClick}>Add</button>
        </div>
        </>
    )
}

export default App;

 

ل.png

  • 0
نشر
بتاريخ 16 دقائق مضت قال Hxfhf Ucicic:

لقد رفعت الخطافات الى الاعلى وظهر اخطاء اكثر

 import React,{useState,useRef, useEffect} from "react";
 import './App.css'
 import logo from './assets/react.svg'
function App(){
    const [todos , setTodos] = useState([]);
    const inpRef = useRef();
 /// قراءة البيانات من التخزين المحلي عند تحميل الصفحة أول مرة
  useEffect(() => {
    const storedTodos = localStorage.getItem("todos");
    if (storedTodos) {
      setTodos(JSON.parse(storedTodos));
    }
  }, []);

  // حفظ المهام في التخزين المحلي عند كل تغيير في القائمة
  useEffect(() => {
    localStorage.setItem("todos", JSON.stringify(todos));
  }, [todos]); 

    const handelButtonClick = () => {
        const text = inpRef.current.value;
        const newTodos = {compiled: false, text}
        inpRef.current.value = '';
        setTodos([...todos,newTodos])
    }

    const handelItemClick = (i) => {
        const newItem = [...todos];
        newItem[i].compiled = !newItem[i].compiled;
        setTodos(newItem)
    }

    const handelDeleteClick = (i) => {
        const newDelete = [...todos];
        newDelete.splice(i,1)
        setTodos(newDelete);
    }
    return(
        <>
        <img src={logo} alt="" />
        <div className="App">
            <h2>To Do List</h2>
            <ul>
            {todos.map(({text,compiled}, i) => {
                return <div className="item"><li key={i} className={compiled ? 'done' : ''}  onClick={() => handelItemClick(i)}>{text}</li><span onClick={() => handelDeleteClick(i)}></span></div>
            })}
            </ul>
            <input ref={inpRef} placeholder="Enter item..."/>
            <button  onClick={handelButtonClick}>Add</button>
        </div>
        </>
    )
}

export default App;

المشكلة في دالة الدالة JSON.parse()، فالنص الذي تستقبله ليس بصيغة JSON صحيحة، أي هنا:

setTodos(JSON.parse(storedTodos));

غالبًا قمت  بتخزين بيانات غير صحيحة في التخزين المحلي من قبل، حيث يوجد دالة في  Local Storage بدلاً من قائمة المهام.

اضغط على F12 ثم توجه إلى تبويب Application في أدوات المطور، ثم اختر  Local Storage وستجد التخزين الخاص بعنوان localhost:3000 مثلاً، فاضغط عليه ثم قم بالحذف بالضغط على الزر التالي:

2025-06-27_23-07-52.thumb.PNG.25575e4e48874466fb9bedc810fc7648.PNG

بعد ذلك أعد تحديث الصفحة وقم بحفظ البيانات والتجربة من جديد.

 

  • 0
نشر
بتاريخ الآن قال Hxfhf Ucicic:

قمت بحذف بيانات ومازالت المشكلة موجودة

هل قمت بتحديث الصفحة؟ عامًة تفقد ما هي البيانات التي يتم جلبها من التخزين من خلال الكونسول كالتالي:

    useEffect(() => {
        const storedTodos = localStorage.getItem("todos");
        
        console.log("القيمة المخزنة التي تمت قراءتها:", storedTodos); //هنا 
        
        if (storedTodos) {
            setTodos(JSON.parse(storedTodos));
        }
    }, []);

وتفقد المشكلة، ثم تستطيع إرفاق مجلد المشروع لتفقده.

  • 0
نشر
بتاريخ منذ ساعة مضت قال Hxfhf Ucicic:

المشروع يعمل دون أى مشكلة لقد قمت بتجربته ولا تحدث به أى أخطاء.

ولكن توجد مشكلة في المنطق حيث عند تحديث الصفحة يتم حذف جميع المهام لتصبح قائمة المهام فارغة في التخزين المحلي.

والسبب هو إستخدامك لإثنين من useEffect حيث ال useEffect الثانية يتم تنفيذها في البداية والتي يكون فيها قيمة todos بمصفوفة فارغة .

وإليك الكود التالي الصحيح للحل :

import React, { useState, useRef, useEffect } from "react";
import './App.css'
import logo from './assets/react.svg'
function App() {
  const [todos, setTodos] = useState([]);
  const inpRef = useRef();
  /// قراءة البيانات من التخزين المحلي عند تحميل الصفحة أول مرة
  useEffect(() => {
    const storedTodos = localStorage.getItem("todos",);
    if (storedTodos) {
      setTodos(JSON.parse(storedTodos));
    }
  }, []);

  // حفظ المهام في التخزين المحلي عند كل تغيير في القائمة
  // useEffect(() => {
  //   localStorage.setItem("todos", JSON.stringify(todos));
  // }, [todos]); 

  const handelButtonClick = () => {
    const text = inpRef.current.value;
    const newTodos = { compiled: false, text }
    inpRef.current.value = '';
    setTodos([...todos, newTodos])
    localStorage.setItem("todos", JSON.stringify([...todos, newTodos]));
  }

  const handelItemClick = (i) => {
    const newItem = [...todos];
    newItem[i].compiled = !newItem[i].compiled;
    setTodos(newItem)
    localStorage.setItem("todos", JSON.stringify(newItem));

  }

  const handelDeleteClick = (i) => {
    const newDelete = [...todos];
    newDelete.splice(i, 1)
    setTodos(newDelete);
    localStorage.setItem("todos", JSON.stringify(newDelete));

  }
  return (
    <>
      <img src={logo} alt="" />
      <div className="App">
        <h2>To Do List</h2>
        <ul>
          {todos.map(({ text, compiled }, i) => {
            return <div className="item"><li key={i} className={compiled ? 'done' : ''} onClick={() => handelItemClick(i)}>{text}</li><span onClick={() => handelDeleteClick(i)}></span></div>
          })}
        </ul>
        <input ref={inpRef} placeholder="Enter item..." />
        <button onClick={handelButtonClick}>Add</button>
      </div>
    </>
  )
}

export default App;

 

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

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

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

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

  • إعلانات

  • تابعنا على



×
×
  • أضف...