أحمد حبنكة

الأعضاء
  • المساهمات

    338
  • تاريخ الانضمام

  • تاريخ آخر زيارة

السُّمعة بالموقع

209 Excellent
  1. يمكن محاكاة جميع lifecycle methods باستخدام الخطاف useEffect فمثلاً يمكن استبدال الكلاس التالي: class A extends React.Component{ constructor(props){ super(props); this.state = { mousePos: {x: 0,y: 0} }; this.handleMouseMove = this.handleMouseMove.bind(this); } handleMouseMove(ev){ this.setState({mousePos: {x: ev.offsetX, y: ev.offsetY}}); } componentDidMount(){ window.addEventListener("mousemove",this.handleMouseMove); } componentWillUnmount(){ window.removeEventListener("mousemove",this.handleMouseMove); } render(){ return <p>Mouse Pos: ({this.state.mousePos.x},{this.state.mousePos.y})</p> } } بالـfunctional Component التالية: function A(){ const [mousePos,setMousePos] = useState({x: 0,y: 0}); function handleMouseMove(ev){ setMousePos({x: ev.offsetX,y: ev.offsetY}); } useEffect(() => { window.addEventListener("mousemove",handleMouseMove); return () => { window.removeEventListener("mousemove",handleMouseMove); } },[]); return <p>Mouse Pos: {mousePos.x,mousePos.y}</p>; } لنشرح مثالنا بشكل مبسط، كود componentDidMount صار داخل useEffect وكود componentWillUnmount صار داخل ما يسمى cleanup function وهو تابع نرده من داخل التابع الممرر للـuseEffect. ولكن ما هذه المصفوفة التي نمررها في المعامل الثاني؟!!! للحقيقة تابع useEffect أقوى بكثير من توابع lifecycle ولذلك هذه المصفوفة موجودة وبرأيي لتفهم الخطاف useEffect حقاً فكر فيه بالطريقة التالية: عند استدعاء الخطاف useEffect اﻻستدعاء التالي: useEffect(() => { doSomethingWithAAndB(a,b); return () => { doCleanUpWithAAndB(a,b); }; },[a,b]); فإننا نقول للـReact "كلما تغيرت قيمة a أو قيمة b نفذ التابع doSomethingWithAAndB، أوه ولا تنس تنفيذ التابع doCleanUpWithAAndB قبل ذلك لتنظيف آثار تنفيذ التابع doSomethingWithAAndB بالقيم القديمة"، فلنشرح هذه الجملة ولنفترض القيم البدائية لـa هي 0 وb هي 0 أي هما رقمان وبالنسبة لـ من أين أتى المتحولان a وb ففي غالب اﻷحيان هما معرفان بالشكل اﻵتي: const [a,setA] = useState(0); const [b,setB] = useState(0); لكن قد يكونان معرفين على شكل useMemo أو أي شيء آخر، المهم أن هناك طريقة لتغيير قيمة a أو قيمة b تتسبب بعملية rerender للـReact Component. اﻵن قيمة a هي 0 وقيمة b هي 0، عند أول render يستدعى الخطاف useEffect حينها التابع doSomethingWithAAndB ينفذ ممرراً له القيمتان 0 و0. لنفترض أننا قمنا بعمل setA إلى القيمة 1 بفعل زر ما في الواجهة أو ما شابه، لقد تغيرت قيمة a اﻵن من 0 إلى 1 ولدينا rerender إذاً أولاً ننفذ التابع doCleanUpWithAAndB ممررين له القيمة القديمة لـa أي 0 وقيمة b التي لم تتغير 0، بعد ذلك يستدعى التابع doSomethingWithAAndB بالقيم الجديدة أي نمرر 1 و 0 كمعاملين لهذا التابع. عند تغيير قيمة b يحصل نفس الشيء وهكذا، أما عندما يحدث unmount للـReact Component ينفذ التابع doCleanupWithAAndB بالقيم الحالية لـa وb فقط ولا ينفذ doSomethingWithAAndB. معقد أليس كذلك؟ نعم أعترف بأن الخطاف useEffect معقد ولكن ما إن تفهم كيف يعمل ستجد بأنه أجمل بكثير من lifecycle methods. أخيراً أشجعك على قراءة المزيد عن هذا الخطاف من هذا الرابط: https://wiki.hsoub.com/React/hooks_effect
  2. ﻷن حالتك وحالات مشابهة لها تصير سهلة جداً بـredux-toolkit هذا عدا عن تنظيم كود redux بشكل جميل بالطبع.
  3. الطريقة الوحيدة لعمل ما تريده في حالتك هي نسخ المصفوفة قبل التعديل عليها هكذا: const newmyAllAds = [...state.myAllAds]; newmyAllAds[action.payload].isFav = false; return { ...state, myAllAds: newmyAllAds, myFavorite: [...state.myFavorite, action.payload], }; وبهذا يتم التعديل. ولكن لماذا لا تتعلم redux-toolkit وحينها بدلاً من كل هذا التعقيد يصير تابع add شبيهاً بالتالي: const item = state.myFavorite.find( (item: ItemInter) => item.id === action.payload.id, ); if (item) return; state.myFavorite.push(action.payload); state.myAllAds[action.payload].isFav = false;
  4. هذا السطر: for(let z in tasks) لن يعطيك النتيجة التي تريدها ﻷن tasks عقدة DOM وليست array أو ما شابه. ما نريده هو هذا: function TP(){ // for(let z=1;z<tasks.childElementCount;z++) for(let task in tasks.childNodes) { if(filter.value==task.childNodes[0].textContent) console.log('hello'); } // console.log(filter.value,typeof(filter.value)); // console.log(tasks.childNodes[1].childNodes[0].textContent,typeof(tasks.childNodes[1].childNodes[0].textContent)) } أي أننا نمر على childNodes collection وليس على DOM object، لاحظ الفرق بين collection وobject. لا أدري لماذا ظننت أن for(let z in tasks) سترد لك عداداً، إن for..in على object بغض النظر إن كان object عادي أو DOM object سوف يعطيك properties وmethods الخاصة بهذا الـobject أي أن for(let z in tasks) مطابق من حيث الفكرة للتالي: const obj = {a: 2,b: 3,c: () => {return this.a + this.b}}; for(let z in obj){ console.log(z); // a then b then c the name of the key }
  5. بما أن عقد i تضاف ديناميكياً فقبل إضافتها أي قبل استدعاء التابع newTask هي غير موجودة ضمن document لذلك التعليمة التالية: document.querySelectorAll("i").addEventListener("click",icon) لن تعمل، فلنزلها وننظر أين نقوم بإضافة عقدة i إلى document ؟ هنا: //new i in newdiv let newi=document.createElement('i'); newdiv.appendChild(newi); newi.setAttribute("class", "bi bi-x-square-fill"); هنا يمكننا تعريف onclick بإحدى طريقتين إما: //new i in newdiv let newi=document.createElement('i'); newdiv.appendChild(newi); newi.setAttribute("class", "bi bi-x-square-fill"); newi.onclick = icon; أو نستخدم addEventListener هكذا: //new i in newdiv let newi=document.createElement('i'); newdiv.appendChild(newi); newi.setAttribute("class", "bi bi-x-square-fill"); newi.addEventListener('click',icon); وعلى فكرة يمكن جعل appendChild في النهاية هكذا: //new i in newdiv let newi=document.createElement('i'); newi.setAttribute("class", "bi bi-x-square-fill"); newi.addEventListener('click',icon); newdiv.appendChild(newi); فقد جرت العادة أننا نجهز العقدة أولاً ثم نضيفها إلى document. المعنى من كل هذا أن ناتج الدالة document.createElement هي عقدة DOM تملك جميع خصائص DOM بما في ذلك onclick حتى ولو لم تضف إلى document بعد أي لم تضف إلى الواجهة التي تظهر للمستخدم.
  6. السبب في ذلك أن الحدث exit هو حدث اﻹنهاء العادي أي من دون أخطاء ومن دون event loops ومن دون kill من أجل التقاط جميع حالات اﻹغلاق بما فيها Ctrl + C وExceptions وغير ذلك نكتب الكود التالي: // attach user callback to the process event emitter // if no callback, it will still exit gracefully on Ctrl-C callback = // some function; // do app specific cleaning before exiting process.on('exit', callback); // catch ctrl+c event and exit normally process.on('SIGINT', function () { console.log('Ctrl-C...'); process.exit(2); }); //catch uncaught exceptions, trace, then exit normally process.on('uncaughtException', function(e) { console.log('Uncaught Exception...'); console.log(e.stack); process.exit(99); }); الكود السابق ينفذ تابع callback عند اﻹغلاق النظامي ويطبع Ctrl-C... ويغلق الـprocess عند الضغط على Ctrl+C وأخيراً في حال حدوث exception يطبع الـstacktrace ويغلق process، طبعاً أنت يمكنك إسناد نفس التابع لجميع اﻷحداث، المهم هو الفكرة.
  7. فهمت عليك أنت تريد للـcheckbox أن يكون افتراضياً checked مع قابلية تغييره، في الغالب أنت تستعمل بالفعل useState أو this.state ولكن لسبب ما تظن أنهما لن يعملا ولكن الحقيقة هي أنهما سيعملان لسبب بسيط جداً: إن تابع useState يقبل معامل هو القيمة اﻻفتراضية لمتحول الحالة. إذاً في حالتك كل ما عليك استعماله هو useState(true) أي نمرر true كقيمة افتراضية لتابع useState. وحتى لو كنت تستعمل class component فعندها تضع القيمة اﻻفتراضية في constructor أي constructor(){ this.state = { checked: true } }
  8. node-sass غير منصوح بها بعد اﻵن إذ ينصح فريق مطوري لغة SASS باستخدام ما يسمى بـDart SASS والتي تنصب باﻷمر التالي: npm i -D sass نعم أعلم أن documentation لجماعة create-react-app يقولون بأن تنصب node-sass ولكن سبب ذلك أن node-sass لم تصر deprecated إلا منذ عهد حديث(شهر أكتوبر الماضي) وdocumentation الخاص بـcreate-react-app يحتاج إلى تحديث.
  9. هل يمكنك رفع المشروع كملف zip؟
  10. أنت تمرر لتابع then تابعاً آخر يرد قيمة فماذا تفعل axios بهذه القيمة التي تردها؟ الجواب هو لا شيء لذلك لا يحدث شيء لديك ﻷن الطلب المرسل للمخدم عبارة عن asynchronous operation أي تحدث بالخلفية ولا يمكن لها أن ترد قيمة من نوع number وإنما فقط من نوع Promise وتابع then يرد Promise آخر وليس كما تظن يرد القيمة داخل Promise. ما عليك فعله داخل then هو استدعاء setState هكذا: class A extends React.Component{ constructor(){ this.state = {} } componentDidMount(){ this.getBTCPrice(); } getBTCPrice() { const url = 'https://api.coinbase.com/v2/prices/BTC-USD/spot'; const self = this; axios.get(url) .then(function (response) { self.setState({price: response.data.data.amount}); }) } render() { return( <div> BTC: {this.state.price} USD </div> ); } } لاحظ أننا نقوم باستدعاء طلب ajax داخل componentDidMount والذي عند انتهائه يقوم بوضع state للـReact. تذكر أن العمليات التي ترد Promise دائماً asynchronous ولا يمكن تحويلها إلى synchronous بأي شكل من اﻷشكال، التابع then لا يقوم بتنفيذ Promise بل يرد Promise آخر ينتهي عند انتهاء سلسلة Promises قبل then.
  11. المشكلة تكمن في أن السيرفر يتوقع من request body أن يكون من نوع application/x-www-form-urlencoded فكيف نرسل بيانات من هذا النوع من خلال axios؟ ببساطة بتحويل الكود التالي: const handleSubmit = async (e) => { e.preventDefault(); const user = {username: 'adam', email: 'adam@gmail.com'}; await axios.post('http://localhost:8080/api/v1/data', user); }; إلى التالي: const handleSubmit = async (e) => { e.preventDefault(); const user = {username: 'adam', email: 'adam@gmail.com'}; await axios.post('http://localhost:8080/api/v1/data', new URLSearchParams(user)); }; عند تمرير object من نمط URLSearchParams إلى axios تفهم axios أنك تريد إرسال فورم وتضع Content-Type بشكل تلقائي على x-www-form-urlencoded. هذا ينفع طالما ليس هنالك file uploads أما إن كان هناك ملفات تريد رفعها إلى جانب الحقول النصية فعليك استخدام FormData هكذا: const handleSubmit = async (e) => { e.preventDefault(); const user = {username: 'adam', email: 'adam@gmail.com'}; const userData = new FormData(); userData.set("username",user.username); userData.set("email",user.email); userData.set("somefile",somefileInputRef.file); await axios.post('http://localhost:8080/api/v1/data', userData); }; عند تمرير FormData إلى axios يتم تمرير Content-Type إلى multipart/form-data ولكن انتبه من أن على السيرفر توقع ذلك والتعامل مع الملفات المرفوعة بطريقة ما.
  12. تابع connect الخاص بـredux لا يعمل على تغليف خطاف بل على تغليف component مثال: import { connect } from 'react-redux'; function Component(props) { const {postForm} = props; // use postForm in some way return (<div>{state}</div>); } const mapDispatchToProps = (dispatch) => { return { postForm: (state) => dispatch(formForm(state)) }; }; export default connect(null, mapDispatchToProps)(Component); ذلك بأن تابع connect يمرر props إلى Component من redux ولا يمررها إلى خطاف. إن كنت قد أردت استعمال إحدى props داخل خطاف يمكنك فعل اﻵتي: import { useState } from 'react'; import { connect } from 'react-redux'; function useForm() { const [state, setState] = useState(initialState); return [state, setState]; } function Component(props){ const [form,setForm] = useForm(); const {postForm} = props; const submitHandler = () => postForm(form); return (<button onClick={submitHandler}></button>) } const mapDispatchToProps = (dispatch) => { return { postForm: (state) => dispatch(formForm(state)) }; }; export default connect(null, mapDispatchToProps)(Component); أخيراً لدى react-redux الخطافين useSelector وuseDispatch واللذين يغنيانك عن connect هكذا: import { useState } from 'react'; import { useDispatch } from 'react-redux'; function useForm() { const [state, setState] = useState(initialState); return [state, setState]; } function Component(){ const [form,setForm] = useForm(); const dispatch = useDispatch(); const submitHandler = () => dispatch(formForm(state)); return (<button onClick={submitHandler}></button>) } export default Component;
  13. السبب في ذلك أن تابع setUsername لا يقوم بتحديث قيمة username آنياً بل يضعها ضمن batch of updates ليقوم ReactJS بتحديث عدة قيم لعدة hooks دفعة واحدة من أجل اﻷداء لذلك بعد انتهاء تابع setUsername من التنفيذ لن تكون قيمة username قد تحدثت بعد لذلك طبعاً سوف يطبع القيمة القديمة.
  14. إن كنت تعني أنك تقوم برسم رابط للملف ولكن الرابط يعرض محتوى الملف بدلاً من تحميله حينها يمكنك استعمال خاصية download كالتالي: <a href="fileurl" download></a> وجود خاصية download بحد ذاتها يخبر المتصفح بأن يحمل الملف بدلاً من عرض محتواه، هذه الخاصية تعمل في كل المتصفحات ما عدا IE، بالنسبة للـIE فلا يوجد حل مع اﻷسف. بالطبع يمكنك إسناد نص لخاصية download هكذا: <a href="fileurl" download="filename.pdf"></a> حينها عند الضغط على الرابط سيتم تحميله باسم filename.pdf. إن كنت تعني شيئاً آخر فأرجو منك توضيح مقصدك.
  15. تشغيل vscode كمسؤول بالغالب هو سبب المشكلة، كما أخبرتك لا تستعمل عدة حسابات في تشغيل webpack ، استعمل حساباً واحداً سواءً كان مسؤولاً أم لا