تم إطلاق React.js الإصدار 16.8 نسبيًا مؤخرًا ، حيث أصبحت السنانير متاحة لنا. يتيح لك مفهوم الخطافات كتابة مكونات وظيفية كاملة باستخدام جميع ميزات React ، ويسمح لك بالقيام بذلك بعدة طرق أكثر راحة مما فعلنا باستخدام الطبقات.
لقد أدرك الكثيرون ظهور السنانير بالنقد ، وفي هذا المقال أود أن أتحدث عن بعض المزايا المهمة التي تقدمها لنا المكونات الوظيفية مع السنانير ، ولماذا يجب أن نتحول إليها.
أنا لن الخوض في تفاصيل استخدام السنانير عمدا. هذا ليس مهمًا جدًا لفهم الأمثلة الواردة في هذه المقالة ؛ فهم عام React يكفي. إذا كنت ترغب في قراءة هذا الموضوع تمامًا ، فالمعلومات عن الخطافات موجودة في الوثائق ، وإذا كان هذا الموضوع ممتعًا ، فسوف أكتب مقالًا بمزيد من التفاصيل حول متى ، وكيفية ، وكيفية استخدام الخطافات بشكل صحيح.
السنانير تجعل إعادة استخدام الكود أسهل
دعونا نتخيل مكونًا يجعل شكلًا بسيطًا. شيء سوف يخرج ببساطة بعض المدخلات ويسمح لنا بتحريرها.
شيء من هذا القبيل ، إذا تم تبسيطه إلى حد كبير ، فسيبدو هذا المكون كصف دراسي:
class Form extends React.Component { state = { // fields: {}, }; render() { return ( <form> {/* */} </form> ); }; }
تخيل الآن أننا نريد حفظ قيم الحقول تلقائيًا عند تغييرها. أقترح حذف إعلانات وظائف إضافية ، مثل shallowEqual
و debounce
.
class Form extends React.Component { constructor(props) { super(props); this.saveToDraft = debounce(500, this.saveToDraft); }; state = { // fields: {}, // , 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> {/* , */} {/* */} </form> ); }; }
نفس المثال ، ولكن مع السنانير:
const Form = () => { // 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> {/* , */} {/* */} </form> ); }
كما نرى ، الفرق ليس كبيرًا جدًا بعد. لقد غيرنا useState
خطاف useState
الحفظ في الصياغة ليس في useEffect
، ولكن بعد تقديم المكون باستخدام خطاف useEffect
.
الفرق الذي أريد إظهاره هنا (يوجد آخرون ، سنناقش أدناه): يمكننا إخراج هذا الرمز واستخدامه في مكان آخر:
// useDraft 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 = () => { // const [fields, setFields] = useState({}); const [draftIsSaving, draftLastSaved] = useDraft(fields); return ( <form> {/* , */} {/* */} </form> ); }
الآن يمكننا استخدام ربط useDraft
الذي useDraft
للتو في مكونات أخرى! هذا ، بالطبع ، مثال مبسط للغاية ، ولكن إعادة استخدام نفس الوظيفة تعد ميزة مفيدة للغاية.
الخطافات تسمح لك بكتابة كود أكثر سهولة.
تخيل مكونًا (في الوقت الحالي ، في شكل فئة) ، والذي يعرض ، على سبيل المثال ، نافذة الدردشة الحالية وقائمة من المستلمين المحتملين ونموذج إرسال رسالة. شيء مثل هذا:
class ChatApp extends React.Component { state = { currentChat: null, }; handleSubmit = (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(` ${this.state.currentChat} `); }); }; render() { return ( <Fragment> <ChatsList changeChat={currentChat => { this.setState({ currentChat }); }} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={this.handleSubmit} /> </Fragment> ); }; }
المثال مشروط للغاية ، لكنه مناسب تمامًا للتظاهر. تخيل إجراءات المستخدم هذه:
- فتح الدردشة 1
- إرسال رسالة (تخيل أن الطلب يستغرق وقتًا طويلاً)
- فتح الدردشة 2
- تلقي رسالة حول الإرسال الناجح:
- "تم إرسال رسالة الدردشة 2"
ولكن تم إرسال الرسالة إلى الدردشة 1؟ حدث هذا بسبب حقيقة أن طريقة الفصل لم تعمل مع القيمة التي كانت في وقت الإرسال ، ولكن مع تلك التي كانت موجودة بالفعل في وقت اكتمال الطلب. لن تكون هذه مشكلة في مثل هذه الحالة البسيطة ، ولكن تصحيح مثل هذا السلوك في المقام الأول سوف يتطلب عناية إضافية ومعالجة إضافية ، وثانيا ، قد يكون مصدرًا للأخطاء.
في حالة المكون الوظيفي ، يختلف السلوك:
const ChatApp = () => { const [currentChat, setCurrentChat] = useState(null); const handleSubmit = useCallback( (messageData) => { makeSomeAPICall(SEND_URL, messageData) .then(() => { alert(` ${currentChat} `); }); }, [currentChat] ); render() { return ( <Fragment> <ChatsList changeChat={setCurrentChat} /> <CurrentChat id={currentChat} /> <MessageForm onSubmit={handleSubmit} /> </Fragment> ); }; }
تخيل نفس إجراءات المستخدم:
- فتح الدردشة 1
- إرسال رسالة (يستمر الطلب مرة أخرى)
- فتح الدردشة 2
- تلقي رسالة حول الإرسال الناجح:
- "تم إرسال رسالة الدردشة 1"
إذن ما الذي تغير؟ ما تم تغييره هو أنه الآن بالنسبة لكل عرض تختلف فيه currentChat
، فإننا بصدد إنشاء طريقة جديدة. يسمح لنا هذا بعدم التفكير مطلقًا فيما إذا كان هناك شيء سيتغير في المستقبل - نحن نعمل مع ما لدينا الآن . كل مكون تجسيد يغلق في حد ذاته كل ما يتعلق به .
السنانير تنقذنا من دورة الحياة
يتداخل هذا العنصر مع العنصر السابق. React هي مكتبة لوصف واجهة بشكل تعريفي. تسهل القدرة على التصريح إلى حد كبير من كتابة ودعم المكونات ، وتتيح لك التفكير بشكل أقل فيما يجب عمله إذا لم نستخدم React.
على الرغم من ذلك ، عند استخدام الفصول الدراسية ، نواجه دورة حياة المكون. إذا لم تكن أكثر عمقًا ، فسيبدو كما يلي:
- جبل مكون
- تحديث المكون (عند تغيير
state
أو props
) - إزالة المكون
يبدو الأمر مريحًا ، لكنني مقتنع بأنه مريح بسبب العادة. هذا النهج ليس مثل رد الفعل.
بدلاً من ذلك ، تسمح لنا المكونات الوظيفية ذات الخطافات بكتابة مكونات ، لا نفكر في دورة الحياة ، ولكن حول المزامنة . نكتب الوظيفة بحيث تعكس نتيجتها بشكل فريد حالة الواجهة اعتمادًا على المعلمات الخارجية والحالة الداخلية.
useEffect
، الذي ينظر إليه الكثيرون كبديل مباشر لـ componentDidMount
و componentDidUpdate
وما إلى ذلك ، مخصص فعليًا لآخر. عند استخدامه ، نخبر رد الفعل نوعًا ما: "بعد تقديم هذا ، يرجى تنفيذ هذه التأثيرات."
فيما يلي مثال جيد على كيفية عمل المكون مع عداد النقرات من مقالة كبيرة حول useEffect :
- الرد: قل لي ما يجب تقديمه مع هذه الحالة.
- المكون الخاص بك:
- إليك نتيجة العرض:
<p> 0 </p>
. - والرجاء تنفيذ هذا التأثير عند الانتهاء:
() => { document.title = ' 0 ' }
.
- الرد: حسنا. تحديث الواجهة. مرحبًا ، المتصفح ، أقوم بتحديث DOM
- المتصفح: عظيم ، وجهت.
- الرد: سوبر ، والآن سأتصل بالتأثير الذي تلقيته من المكون.
- يبدأ
() => { document.title = ' 0 ' }
أكثر بكثير من التصريح ، أليس كذلك؟
النتائج
React Hooks تتيح لنا التخلص من بعض المشاكل وتسهيل فهم المكونات وترميزها. تحتاج فقط إلى تغيير النموذج العقلي الذي نطبقه عليها. المكونات الوظيفية هي وظائف واجهة المعلمات أساسا. يصفون كل شيء كما ينبغي أن يكون في أي وقت معين ، ويساعدون على عدم التفكير في كيفية الاستجابة للتغييرات.
نعم ، في بعض الأحيان تحتاج إلى معرفة كيفية استخدامها بشكل صحيح ، ولكن بنفس الطريقة ، لم نتعلم كيفية استخدام المكونات في شكل فئات على الفور.