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

أحمد حبنكة

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

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

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

كل منشورات العضو أحمد حبنكة

  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. أنت تمرر لتابع 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.
  10. المشكلة تكمن في أن السيرفر يتوقع من 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 ولكن انتبه من أن على السيرفر توقع ذلك والتعامل مع الملفات المرفوعة بطريقة ما.
  11. تابع 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;
  12. السبب في ذلك أن تابع setUsername لا يقوم بتحديث قيمة username آنياً بل يضعها ضمن batch of updates ليقوم ReactJS بتحديث عدة قيم لعدة hooks دفعة واحدة من أجل اﻷداء لذلك بعد انتهاء تابع setUsername من التنفيذ لن تكون قيمة username قد تحدثت بعد لذلك طبعاً سوف يطبع القيمة القديمة.
  13. إن كنت تعني أنك تقوم برسم رابط للملف ولكن الرابط يعرض محتوى الملف بدلاً من تحميله حينها يمكنك استعمال خاصية download كالتالي: <a href="fileurl" download></a> وجود خاصية download بحد ذاتها يخبر المتصفح بأن يحمل الملف بدلاً من عرض محتواه، هذه الخاصية تعمل في كل المتصفحات ما عدا IE، بالنسبة للـIE فلا يوجد حل مع اﻷسف. بالطبع يمكنك إسناد نص لخاصية download هكذا: <a href="fileurl" download="filename.pdf"></a> حينها عند الضغط على الرابط سيتم تحميله باسم filename.pdf. إن كنت تعني شيئاً آخر فأرجو منك توضيح مقصدك.
  14. تشغيل vscode كمسؤول بالغالب هو سبب المشكلة، كما أخبرتك لا تستعمل عدة حسابات في تشغيل webpack ، استعمل حساباً واحداً سواءً كان مسؤولاً أم لا
  15. هذه المشكلة لن تحدث عندنا ﻷنها مشكلة أذونات، يخبرك webpack أنه لا يملك اﻹذن لفتح الملف C:\Users\abdu1\Desktop\company\build\images\src\images\logo.png. سبب ذلك على اﻷغلب أن هناك برنامجاً آخر يفتح هذا الملف أو يعدل عليه، ربما شغلت webpack وبالخطأ تحاول تشغيله مرةً أخرى. قد يكون سبب ذلك أيضاً مشكلة في الصلاحيات، ربما أنشأ ملف الصورة حساب مستخدم آخر وأنت لا تملك الصلاحية لفتح هذا الملف، هل أنت متأكد من أنك سجلت الدخول باسم المستخدم abdu1؟ إذا كنت تقلب بين المستخدمين عند تشغيل webpack فلا تفعل ذلك مرةً أخرى، دوماً شغل webpack بنفس اسم المستخدم. عدا ذلك لا أجد أي احتمالات أخرى لمشكلتك.
  16. ما تطلبه غير ممكن في React ﻷن ما يجب تمريره للـonClick ليس كود جافاسكريبت كـstring وإنما function object. بالطبع لا يشترط تعريف دالة منفصلة، يمكنك فعل اﻵتي: <button onClick={() => {function1(); function2();}}>click me</button> نعم يمكنك تمرير arrow function إلى onClick بسهولة واستدعاء التابعين الذين تريدهما. هذا ينطبق على كل الخصائص التي تتوقع تابعاً مثل onChange وغيرها من اﻷحداث.
  17. بعكس ما يظنه البعض نعم تخزين token في localStorage غير آمن حتى وإن كنت تستخدم ReactJS، إذاً أين أخزنها ﻷن السيرفر يحتاجها؟ قل لجماعة السيرفر أن يخزنوها بالـCookies وليجعلوها Http-Only أي لا يمكن قراءتها من الجافاسكريبت ولينسوا ذاك Authorization Header ﻷنه يجبر مبرمج frontend على استخدام طرق غير آمنة لتخزين token. وﻷن Cookie لا يمكن قراءتها بالجافاسكريبت لذلك خزن في localStorage عند تسجيل الدخول الناجح متحول بولياني وضعه على true، هذا فقط من أجل واجهة المستخدم حتى لا يضطر المستخدم لتسجيل الدخول مع كل request وليس للأمان واﻷمان يتحقق منه السيرفر من خلال التحقق من Cookies المرسلة مع request. لا تقلق بشأن الـAJAX requests التي تقوم بها ﻷن Cookies ترسل بشكل تلقائي مع request ولا داعي ﻷن تقوم بذلك بنفسك إلا إذا كنت ترسل cross-domain request حينها كل ما عليك هو تمرير withCredentials: true ضمن request config لمكتبة axios مثال: axios.get('https://domaindifferentfromours.com', { withCredentials: true }); بالطبع ما قلته هو الحل المثالي ولكن الواقع قد يفرض عليك تخزين JWT Token ضمن localStorage وهذا هو الحال عند استخدام third-party API مثلاً حيث لا يمكنك التحكم بالسيرفر.
  18. السبب في عدم مقدرتك على استخدام نفس المتحول هو أن case بشكل افتراضي لا تعرف scope جديد لذلك كل متحول يعرف داخلها هو فعلياً يعرف داخل scope الخاص بالـswitch كونك تستخدم let وليس var. لكن حل مشكلتك أسهل بكثير مما تظن وهو كالتالي: const cartReducer = (state = initialState, action) => { switch (action.type) { case QNT_UP:{ let index; //..... break; } case QNT_DOWN:{ let index; //..... break; } default: return state; } }; بتعريف block جديد أمام case صار لدينا scope جديد مختلف عن scope الخاص بالـswitch وكل case لها scope جديد مختلف اﻵن لذلك ستعمل تعليمة let ولن يحدث خطأ. بالطبع الطريقة السابقة ستعمل فقط باستخدام let أو const وليس var ﻷن المتحولات المعرفة بـvar تنتمي للـscope الخاصة بالتابع الذي يعرف فيه المتحول.
  19. يفضل بدلاً من string أن تستعمل الطريقة التالية مباشرةً: const myElement = <div>simple text and <strong>strong text</strong></div>; ما هذا ؟ منذ متى كنا نستطيع إسناد كود JSX إلى متحول؟ تذكر أن كود JSX ما هو في الحقيقة إلا استدعاء تابع من ReactJS والكود أعلاه يترجم إلى التالي: const myElement = React.createElement("div", null, "simple text and ", React.createElement("strong", null, "strong text")); إذاً فكود JSX ما هو إلا expression ويمكن إسناده لمتحول. هكذا نتجنب dangerouslySetInnerHTML والتي سميت بذلك ﻷنها تعرضك لخطر XSS الذي من خلاله قد يهكر شخص ما موقعك ليسرق بيانات مستخدم أو يظهر محتوى يشوه سمعة موقعك وغير ذلك. أما إن كان كود HTML يأتي من دخل المستخدم كما هو الحال في HTML Editors فلا يوجد لديك خيار سوى استعمال dangerouslySetInnerHTML واتخاذ إجرائيات مختلفة للحماية ضد XSS مثل عمليات تحويل على كود HTML قبل عرضه أو غير ذلك من اﻷمور. ملحوظة: بالنسبة للـReact.createElement فهو ينطبق على نسخة ReactJS 16 وما دون، مع ReactJS 17 أصبح اسم التابع مختلفاً ولكن هذا لا يهم لأن الفكرة هي نفسها.
  20. هل يمكنك أن ترينا كود Component ﻷن الطريقة اﻷخيرة يفترض أن تكون صحيحة؟ بالنسبة للإسناد إلى this.state فلا يجوز ذلك إلا في الباني وأي تعديل على this.state خارج الباني سيتجاهله الـReactJS ولن تعدل الواجهات وفقاً للتغيير. ذلك بأن this.state ليس متحولاً سحرياً بل هو مجرد object فكيف للـReactJS أن يعلم أنك عدلت عليه؟ بالنسبة لسؤال عن تغيير بنية الكائن فلا لا داعي لتغيير بنية الكائن وكما قلت لك الطريقة اﻷخيرة وهي التالية: this.setState({person: {name: 'Ahmed'}}) صحيحة والمشكلة في الغالب في بقية كود Component الذي أرجو منك نشره لنحل المشكلة.
  21. الطريقة اﻷصح هي التالية: class ClassComponent extends React.Component{ constructor(){ this.clickHandler = this.clickHandler.bind(this); } // دالة عادية يتم تنفيذها عند الضغط على العنصر p clickHandler(e){ alert('Hello, World!'); } render(){ return <p onClick={this.clickHandler}>Content</p> } } لاحظ أننا نقوم بعمل binding في الباني وليس داخل render، بالنسبة للطرق الثلاثة فاﻷولى ستعمل بشكل صحيح ﻷنها تجنبك خطأ setState on undefined ولكن عمل binding عند كل render سيؤذي اﻷداء لذلك نقوم بها في الباني وليس في render. بالنسبة للطريقتين اﻷخريتين فهما صحيحتان شرط أن تقوم بعمل bind داخل الباني ، من دون bind سيظهر لك خطأ setState on undefined. مع وجود bind في الباني الطريقتان الثانية والثالثة كلاهما صحيحتان وشخصياً أفضل الطريقة الثالثة ﻷنها أقصر وﻷن الطريقة الثانية تنشئ تابع جديد عند كل render أيضاً مما قد يؤذي اﻷداء في بعض الحالات.
  22. المعذرة ولكن هل أنت تستخدم redux-toolkit؟ ﻷن الـsyntax يبدو مشابهاً لها إذ لا أرى أي تعليمة switch، إن كنت تستعملها فاحذف العنصر من المصفوفة كما تفعل في الجافاسكريبت العادية هكذا: DELETE_PLATE: (state, action) => { const actionIndex = state.plates.findIndex(plate => plate === action.payload); state.plates.splice(actionIndex); } لاحظ جمال redux-toolkit ، إنها تسمح لك أن تعدل على state مباشرةً دون المساس بمبادئ redux وذلك ﻷن redux-toolkit تستعمل مكتبة immerjs التي تسهل عليك نسخ object وبالتالي تحقيق القاعدة التي تنص على أن state يجب أن يكون immutable. لكن إن كنت لا تستعمل redux-toolkit فالطريقة لحذف عنصر من مصفوفة هي بنسخ المصفوفة أولاً، حذف العنصر ثم إرجاعها هكذا: DELETE_PLATE: (state, action) => { const actionIndex = state.plates.findIndex(plate => plate === action.payload); const newPlates = [...state.plates]; // this way we copy state.plates, newPlates !== state.plates will be true newPlates.splice(actionIndex); // since newPlates is a copy we can modify it return {...state,plates: newPlates}; } مع أنني لا أدري من أين أتيت بهذا syntax الغريب إن لم تكن تستعمل redux-toolkit ، هل الكود السابق pseudo-code ؟ على كل حال أنا أنصح باستخدام redux-toolkit في جميع مشاريع الـredux وهذا ما ينصح به مطورو redux أنفسهم.
  23. React native هو تقنية لبرمجة تطبيقات الجوال اﻷندرويد واﻵيفون وليس تقنية لتطوير مواقع الوب بعكس React. صحيح أن اﻻثنين لديهما الكثير من التشابهات من حيث المبادئ لكن مجالهما مختلف، إذا كنت تريد أن تصير مطور وب تعلم ReactJS وليس React native، إن كنت تريد تطوير تطبيقات الجوال بالـReact Native حينها تعلمه. طبعاً React Native هو أحد تقنيات تطوير الجوال وهناك تقنيات أخرى لا تستخدم الجافاسكريبت مثل تقنية flutter التي تستعمل لغة Dart كلغة برمجة بدلاً من الجافاسكريبت، هناك أيضاً طريقة الـnative التي تستعمل Java/Kotlin للأندرويد وSwift/Objective C للآيفون.
  24. السطر الذي أضفته ليس الطريقة الصحيحة بل هذه: this.increaseCounter = this.increaseCounter.bind(this); الهدف اﻷصلي من التعليمة أن arrow function الممرر للـonClick لها قواعد خاصة من ناحية this هذه القواعد تمنع الوصول إلى Component instance وبدلاً من ذلك تكون this مساوية لـundefined. لكن بعد تنفيذ التعليمة السابقة أنت تجعل increaseCounter يأخذ الـthis دائماً من Component instance لهذا سمي التابع بـbind وهو للعلم تابع جافاسكريبت عادي ليس مخصوصاً بالـReactJS ، كل ما هنالك أن استعماله في React class components شائع جداً. بالطبع مع وجود react hooks يمكنك كتابة functional components وحينها لن تحتاج إلى عمل أي binding ﻷنه حينها لا يوجد class لتقوم بعمل this.
×
×
  • أضف...