مكونات وظيفية مع رد فعل السنانير. لماذا هو أفضل؟

عندما تم إصدار React.js 16.8 ، سنحت لنا الفرصة لاستخدام React Hooks. الخطافات تجعلنا قادرين على كتابة مكونات تعمل بكامل طاقتها باستخدام الوظائف. يمكننا استخدام جميع ميزات React.js والقيام بطريقة أكثر ملاءمة.


كثير من الناس لا يتفقون مع مفهوم السنانير. في هذه المقالة أود أن أخبر عن بعض المزايا الهامة التي تقدمها لك React Hooks ولماذا نحتاج إلى الكتابة باستخدام Hooks.


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


السنانير تسمح لنا لإعادة استخدام رمز لدينا بسهولة


دعونا نتخيل مكونًا يقدم نموذجًا بسيطًا. يمكن أن يكون شيئًا يوضح لنا بعض المدخلات ويسمح لنا بتغيير قيمها.


مع تدوين الفصل ، سيكون هناك شيء مثل هذا:


class Form extends React.Component { state = { // Fields values fields: {}, }; render() { return ( <form> {/* Inputs render */} </form> ); }; } 

دعنا الآن نتخيل أننا نريد حفظ قيم حقولنا تلقائيًا في الواجهة الخلفية كلما تغيرت. أقترح تخطي تعريف الوظائف الخارجية مثل shallowEqual و debounce .


 class Form extends React.Component { constructor(props) { super(props); this.saveToDraft = debounce(500, this.saveToDraft); }; state = { // Fields values fields: {}, // Draft saving meta draft: { isSaving: false, lastSaved: null, }, }; saveToDraft = (data) => { if (this.state.isSaving) { return; } this.setState({ isSaving: true, }); makeSomeAPICall().then(() => { this.setState({ isSaving: false, lastSaved: new Date(), }) }); } componentDidUpdate(prevProps, prevState) { if (!shallowEqual(prevState.fields, this.state.fields)) { this.saveToDraft(this.state.fields); } } render() { return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); }; } 

نفس العنصر مع السنانير:


 const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

كما نرى ، ليس هناك فرق كبير هنا. this.state مع هوك useState وحفظ المسودة في useEffect hook الآن.


الفرق الذي أريد إظهاره هنا هو (هناك اختلافات أخرى أيضًا ، لكنني سأركز على هذا الاختلاف): يمكننا بسهولة استخراج هذا الرمز من مكوننا واستخدامه في مكان آخر:


 // useDraft hook can be used in any other component const useDraft = (fields) => { const [draftIsSaving, setDraftIsSaving] = useState(false); const [draftLastSaved, setDraftLastSaved] = useState(false); useEffect(() => { const id = setTimeout(() => { if (draftIsSaving) { return; } setDraftIsSaving(true); makeSomeAPICall().then(() => { setDraftIsSaving(false); setDraftLastSaved(new Date()); }); }, 500); return () => clearTimeout(id); }, [fields]); return [draftIsSaving, draftLastSaved]; } const Form = () => { // Our state const [fields, setFields] = useState({}); const [draftIsSaving, draftLastSaved] = useDraft(fields); return ( <form> {/* Draft saving meta render */} {/* Inputs render */} </form> ); } 

ويمكننا استخدام هوك استخدام في مكونات أخرى! إنه ، بالطبع ، مثال بسيط للغاية ، ولكن إعادة استخدام الكود مهمة جدًا ويوضح المثال مدى سهولة استخدام Hooks.


السنانير تسمح لنا لكتابة عنصر بطريقة أكثر سهولة


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


 class ChatApp extends React.Component { state = { currentChat: null, }; handleSubmit = (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${this.state.currentChat}`); }); }; render() { return ( <Fragment> <ChatsList changeChat={currentChat => { this.setState({ currentChat }); }} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={this.handleSubmit} /> </Fragment> ); }; } 

ثم تخيل مستخدمنا باستخدام مكون الدردشة هذا:


  • يفتحون الدردشة 1
  • يرسلون رسالة (دعنا نتخيل بعض الشبكات البطيئة)
  • يفتحون الدردشة 2
  • يرون تنبيهًا بشأن رسالةهم:
    • "يتم إرسال الرسالة إلى الدردشة 2"

لكنهم أرسلوا رسالة إلى الدردشة الثانية ، كيف حدث ذلك؟ كان سبب ذلك هو أن طريقة الفصل تعمل وفقًا للقيمة الحالية ، وليس القيمة التي كانت لدينا عندما بدأنا طلب رسالة. إنها ليست مشكلة كبيرة بمكونات بسيطة مثل هذا ، ولكنها يمكن أن تكون مصدرًا للأخطاء في الأنظمة الأكثر تعقيدًا.


في المقابل ، تعمل المكونات الوظيفية بطريقة أخرى:


 const ChatApp = () => { const [currentChat, setCurrentChat] = useState(null); const handleSubmit = useCallback( (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(`Message is sent to chat ${currentChat}`); }); }, [currentChat] ); render() { return ( <Fragment> <ChatsList changeChat={setCurrentChat} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={handleSubmit} /> </Fragment> ); }; } 

دعونا نتخيل مستخدمنا:


  • يفتحون الدردشة 1
  • يرسلون رسالة (دعنا نتخيل بعض الشبكات البطيئة)
  • يفتحون الدردشة 2
  • يرون تنبيهًا بشأن رسالةهم:
    • "يتم إرسال الرسالة إلى الدردشة 1"

حسنا ، ما الذي تغير؟ الآن نحن نعمل مع القيمة ، التي تم التقاطها في لحظة تقديم. نحن بصدد إنشاء handleSubmit كل مرة يتم فيها تغيير handleSubmit . إنها تتيح لنا نسيان التغييرات المستقبلية والتفكير الآن .


يعرض كل مكون التقاط كل شيء يستخدمه .


السنانير تجعل مكونات دورة الحياة ذهبت


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


ومع ذلك ، عندما نستخدم الطبقات ، نواجه دورة حياة المكونات. يبدو مثل هذا:


  • تصاعد
  • تحديث (كلما تغيرت state أو props )
  • إلغاء التثبيت

يبدو ملائمًا لكني مقتنع أنه فقط بسبب عاداتنا. انها ليست مثل رد الفعل.


بدلاً من ذلك ، تتيح لنا المكونات الوظيفية كتابة رمز المكونات ونسيان دورة الحياة. نحن نفكر فقط في التزامن . نكتب وظيفة يجعل واجهة المستخدم لدينا من الدعائم المدخلات والحالة الداخلية.


عند useEffect الأول ، يبدو ربط الخطاف وكأنه بديل لـ componentDidMount و componentDidUpdate وطرق دورة الحياة الأخرى. لكنها ليست مثل هذا. عندما نستخدم useEffect قلنا لـ useEffect : "مهلاً ، اصنع ذلك بعد تقديم مكوّن".


هنا مثال جيد من المقال الكبير حول useEffect :


  • الرد: أعطني واجهة المستخدم عندما تكون الحالة 0 .
  • المكون الخاص بك:
    • إليك نتيجة العرض: <p>You clicked 0 times</p> .
    • تذكر أيضًا تشغيل هذا التأثير بعد انتهائك: () => { document.title = 'You clicked 0 times' } .
  • الرد: بالتأكيد. تحديث واجهة المستخدم. مرحبًا يا مستعرض ، أقوم بإضافة بعض العناصر إلى DOM.
  • المتصفح: بارد ، أنا رسمت على الشاشة.
  • الرد: حسنًا ، الآن سأعمل على التأثير الذي أعطيته لي.
    • Running () => { document.title = 'You clicked 0 times' } .

إنها طريقة أكثر إعلانًا ، أليس كذلك؟


في الختام


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


حسنًا ، نحتاج إلى معرفة كيفية استخدامه ، ولكن مهلا ، هل كتبت مكونات الفصل بشكل صحيح في المرة الأولى؟

Source: https://habr.com/ru/post/ar443500/


All Articles