مرحبا في 24-25 سبتمبر ، تم عقد مؤتمر للمطورين المتقدمين في HolyJs https://holyjs-moscow.ru/ في موسكو. لقد جئنا إلى المؤتمر بموقفنا الذي أجرينا فيه الاختبار. كان هناك اختبار رئيسي - 4 جولات تصفيات وجولة أخيرة ، والتي لعبت عليها Apple Watch وبناة ليغو. وبشكل منفصل ، أجرينا مسابقة رد فعل المعرفة.
تحت القط - تحليل المهام مسابقة على رد فعل. سيتم إخفاء الخيارات الصحيحة تحت المفسد ، لذلك لا يمكنك قراءة التحليل فحسب ، ولكن يمكنك أيضًا التحقق من نفسك :)

دعنا نذهب!
للراحة ، قمنا بتجميع الأسئلة في أقسام:
القسم 1. الفهم الأساسي لتشغيل this.setState وتحديث دورة حياة المكون:
السؤال 1.
react-: 1) SetProps, SetState, ForceUpdate 2) ForceUpdate, SetState 3) ForceUpdate, SetState, Parent (re)render 4) ForceUpdate, SetState, directly call UpdateComponent
الجواب3) ForceUpdate ، SetState ، الأصل (إعادة) تقديم
السؤال 2.
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
الجواب1) تم وضع علامة على العنصر القذر ، يتم استدعاء دورة حياة التحديث
تحليل السؤالين 1 و 2للإجابة على السؤال ، سنقوم بتحليل جزأين:
1) طلب مكون خاص لتحديث دورة
2) طلب خارج المكون
يحتوي المكون نفسه على طريقتين لتحديث نفسه:
1) this.setState و this.forceUpdate. في هذه الحالة ، سيتم وضع علامة على المكون قذرًا وعلى علامة المصالحة ، إذا كانت الأولوية للتقديم ، فستبدأ دورة التحديث.
حقيقة مثيرة للاهتمام: this.setState({})
و this.forceUpdate
مختلفان. عندما يتم this.setState({})
يتم استدعاء حلقة التحديث الكاملة ، على عكس this.forceUpdate
، عندما تبدأ حلقة التحديث دون shouldComponentUpdate. يمكن العثور على مثال على this.setState({})
هنا: https://codesandbox.io/s/m5jz2701l9 (إذا قمت باستبدال setState مع forceUpdate في المثال ، يمكنك معرفة كيفية تغير سلوك المكونات).
2) عند إعادة تقديم أصل المكون ، يقوم بإرجاع الجزء vDOM ، وجميع الأطفال الذين سوف يحتاجون إلى تحديث ، وسوف يطلق عليهم أيضًا دورة حياة تحديث كاملة. يمكن تجنب إعادة فرز الأصوات كاملة من الشجرة الفرعية عن طريق وصف shouldComponentUpdate أو عن طريق تعريف المكون كـ PureComponent.
السؤال 3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
الإجابة والتحليل2) PC تنفذ SCU ، وتجري الدعائم الضحلة والدولة
كما ناقشنا سابقًا ، عند (إعادة) تقديم الأصل ، سيتم إرسال الشجرة الفرعية بالكامل إلى دورة حياة التحديث. تخيل أنك قمت بتحديث عنصر الجذر. في هذه الحالة ، وفقًا لتأثير السلسلة ، سيتعين عليك تحديث شجرة رد الفعل بالكامل تقريبًا. لتحسين التحديثات غير الضرورية وعدم إرسالها ، shouldComponentUpdate
فعل هناك طريقة يجب أن يتم تحديثها ، والتي تسمح لك بالعودة إذا كان المكون يجب تحديثه shouldComponentUpdate
غير ذلك. لتبسيط المقارنة في رد الفعل ، يمكنك أن ترث من PureComponent
فورًا في حالة ما إذا كان يجب على shouldComponentUpdate
، الذي يقارن بالرجوع (إذا كان الأمر يتعلق بأنواع الكائنات) أو حسب القيمة (إذا كان يتعلق بأنواع القيمة) جميع الدعائم والدولة التي تأتي إلى المكون.
السؤال 4.
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
الإجابة والتحليل2) سيتم استدعاء الوظيفة الثانية بعد تحديث الحالة
توجد طريقتان في دورة حياة Reacty: componentDidMount
لحلقة التركيب و componentDidUpdate للتحديث ، حيث يمكنك إضافة بعض المنطق بعد تحديث المكون. على سبيل المثال ، قم بإجراء طلب http ، وقم بإجراء بعض التغييرات في النمط ، واحصل على مقاييس عناصر html ، ثم (set condition). إذا كنت تريد اتخاذ بعض الإجراءات بعد تغيير بعض الحقول في الولاية ، فعندئذٍ في طريقة componentDidUpdate
يجب عليك كتابة أي مقارنة:
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
أو يمكنك القيام بذلك عن طريق setState:
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
كل نهج له إيجابيات وسلبيات (على سبيل المثال ، إذا قمت بتغيير setState في عدة أماكن ، فقد يكون من المناسب أن تكتب شرطًا مرة واحدة).
السؤال 5.
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' onClick={() => console.log('foo')} /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
السؤال 6.
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
السؤال 7.
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
تحليل الأسئلة 5-7الأسئلة 5-7 ضرورية لنفس الشيء - للتحقق من فهم عمل PureComponent
وتحديث المكونات عند نقل الدعائم. إذا قمنا داخل طريقة التجسيد بتمرير رد اتصال jsx ، فيصف ذلك مباشرةً في وظيفة التجسيد:
render () { return <Button onClick={() => {}} />; }
ثم يقوم كل من الوالدين بتحديث معالج النقر المحدد. يحدث هذا لأن كل تجسيد يخلق وظيفة جديدة مع رابط فريد ، والذي عند مقارنته في PureComponent سيظهر أن الدعائم الجديدة لا تساوي تلك القديمة وتحتاج إلى تحديث المكون. في حالة مرور كافة عمليات الفحص وإرجاع يجب على "إرجاع المكون" كاذبة ، لا يحدث التحديث.
القسم 2. مفاتيح في رد الفعل
تحليل مفصل للمفاتيح التي نشرناها هنا: https://habr.com/company/hh/blog/352150/
السؤال 1.
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
الإجابة والتحليل1) احذف المثيل السابق وقم بتركيب المثيل الجديد عند تغيير المفتاح
بدون استخدام المفتاح ، ستقوم الاستجابة بمقارنة قائمة العناصر في أزواج من الأعلى إلى الأسفل. إذا استخدمنا المفتاح ، فستحدث المقارنة على المفتاح المقابل. إذا ظهر مفتاح جديد ، فلن تتم مقارنة هذا المكون بأي شخص وسيتم إنشاؤه على الفور من البداية.
يمكنك استخدام هذه الطريقة حتى لو كان لدينا عنصر واحد: يمكننا تعيين <A key="1" />
، وفي العرض التالي نحدد <A key="2" />
وفي هذه الحالة ، سيؤدي الرد إلى حذف <A key="1" />
وإنشاء من الصفر <A key="2" />
.
السؤال 2.
this.prop.key? 1) 2) 3) static getKey
الإجابة والتحليل2) لا
يمكن للمكون تعلم المفتاح من أبنائه ، والذي تم تمريره إليه كدعامة ، ولكن لا يمكن معرفة المفتاح الخاص به.
السؤال 3.
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A key='1' />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
الإجابة والتحليل2) 2
عند تغيير المفتاح ، سيتم إعادة تكوين المكون ، لذلك سيتم عرض التجسيد مرتين.
القسم 3. أسئلة حول jsx
السؤال 1.
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
الإجابة والتحليل1) رد الاتصال في شكل سند / السياق
2) إزالة طبقة النموذج والعمل من خلالها
هناك نوعان من الإجابات الصحيحة. اختيار أي منهم في مسابقة سوف يحسب لك النقاط. هذا السؤال هو لمعرفة تدفق البيانات تتفاعل. يتم توزيع البيانات من أعلى إلى أسفل في شكل الدعائم أو السياق ، ويمكن أن تحتوي على رد اتصال ، والذي يمكن للمكون أدناه الاتصال به للتأثير على حالة النظام.
هناك طريقة أخرى تجمع بين إزالة النموذج والسياق والدعامة ، على سبيل المثال ، ربط رد فعل رد الفعل.
تأخذ هذه المكتبة نموذجًا مستمدًا من رد الفعل (redux). يضبط redux.store في موفر ، والذي يعين بالفعل مخزن في السياق. ثم يستخدم المطور HOC connect ، والذي يذهب إلى السياق ، ويشترك في تخزين (store.subscribe) ، وعندما يتغير المتجر ، يقوم mapStateToProps
وظيفة mapStateToProps
. إذا كانت البيانات قد تغيرت ، فإنه يضعها في الدعائم إلى كائن ملفوف.
في الوقت نفسه ، يتيح لك الاتصال تحديد mapDispatchToProps
، حيث يحدد المطور تلك الإجراءات التي يجب تمريرها إلى المكون. نحن ، بدورنا ، actionCreators
من الخارج (بدون سياق) ، actionCreators
(نلفهم في store.dispatch) ونمررهم كدعائم إلى المكون المُلف.
السؤال 2.
props jsx? 1) 2) children
الإجابة والتحليل1) في أي
يمكنك نقل إلى أي. على سبيل المثال:
<Button icon={<Icon kind='warning'/>}></Button>
يرسم زر مع أيقونة. يعتبر هذا النهج مناسبًا جدًا للاستخدام من أجل ترك المكون الحق في التحكم في موقع العناصر المختلفة بالنسبة لبعضها البعض ، بدلاً من فرز أحد الأطفال.
القسم 4. فهم متقدم setState
فيما يلي 3 أسئلة مرتبطة بقوة:
السؤال 1.
this.state = {a: 'a'}; ... this.setState({a: 'b'}); this.setState({a: this.state.a + 1}) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'a'}
السؤال 2.
this.state={a: 'a'} ... this.setState({a: 'b'}) this.setState(state => ({a: state.a + 1})) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'ab1'}
السؤال 3.
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
تحليل الأسئلة 1-3تم وصف جميع أعمال setState بالكامل هنا:
1) https://reactjs.org/docs/react-component.html#setstate
2) https://stackoverflow.com/questions/48563650/does-react-keep-the-order-for-state-updates/48610973#48610973
الحقيقة هي أن setState لا يحدث بشكل متزامن.
وفي حالة وجود عدة مكالمات لضبط الحالة في صف واحد ، اعتمادًا على ما إذا كنا داخل طريقة دورة حياة رد الفعل ، فإن وظيفة معالج حدث التفاعل (onChange ، onClick) أم لا ، يعتمد تنفيذ setState.
داخل معالجات رد الفعل ، يعمل setState على دفعات (يتم تغيير التغييرات فقط بعد انتهاء الوظائف المعرفة من قبل المستخدم في مكدس الاستدعاءات وننضم إلى الوظائف التي تسمى أساليب معالج الأحداث وطريقة دورة حياتنا). تتدحرج الواحدة تلو الأخرى ، لذلك إذا كنا داخل معالج التفاعل ، فسنحصل على:
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + . this.state.a // a: 'a' this.setState({a: this.state.a + 1}) // a: 'a1'
منذ التغييرات وقعت batchevo.
ولكن في نفس الوقت ، إذا تم استدعاء setState خارج معالجات التفاعل:
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + this.state.a // a: 'b' this.setState({a: this.state.a + 1}) // a: 'b1' +
بما أنه في هذه الحالة ، سيتم إرجاع التغييرات بشكل منفصل.
القسم 5. استرجاع
السؤال 1.
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
الإجابة والتحليل3) نعم ، تحتاج إلى تحديد الوسيطة المخصصة لمثل هذا الإجراء
خذ عملية إعادة - thock أبسط مثال. جميع البرامج الوسيطة عبارة عن كتلة صغيرة من التعليمات البرمجية:
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js#L2-L9
return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };
كيف تعمل الوسيطة؟
يحصلون على السيطرة قبل وصول الإجراء إلى المتجر. لذلك ، فإن الإجراء الذي تم الكتابة فوقه سيتم أولاً الانتقال عبر سلسلة البرامج الوسيطة.
يقبل كل برنامج وسيط مثيل متجر ، والطريقة التالية ، التي تتيح لك إعادة توجيه الإجراء إلى أبعد من ذلك والإجراء نفسه.
إذا عالجت الوسيطة الإجراءات المخصصة ، مثل redux-thunk ، فإذا كانت هذه الوظيفة هي وظيفة ، فلن تقوم بإعادة توجيه الإجراء إلى أبعد من ذلك ، ولكنها "تغرق" ، بدلاً من ذلك ، تستدعي الإجراء باستخدام أساليب الإرسال و getState التي تم تمريرها إلى هناك.
ماذا سيحدث لو حدث رد فعل thux بعد العملية ، وهي وظيفة؟
قبل استدعاء المخفضات ، يخزن المتجر نوع الإجراء. يجب أن يستوفي الشروط التالية:
1) يجب أن يكون كائن
2) يجب أن يكون حقل نوع
3) يجب أن يكون حقل الكتابة من نوع السلسلة
إذا لم يتم استيفاء أحد الشروط ، فسوف يؤدي الإرجاع إلى حدوث خطأ.
أسئلة مكافأة:
مكافأة السؤال 1.
? class Todos extends React.Component { getSnapshotBeforeUpdate(prevProps, prevState) { return this.props.list.length - prevProps.list.length; } componentDidUpdate(a, b, c) { console.log(c); } ... } ReactDOM.render(<Todos list={['a','b']} />, app); setTimeout(() => ReactDOM.render(<Todos list={['a','b','a','b']} />, app), 0); a) 0 b) 1 c) 2 d) undefined
الإجابة والتحليلج) 2
getSnapshotBeforeUpdate
هي وظيفة نادرا ما تستخدم في رد الفعل والتي تسمح لك بالحصول على لقطة ، والتي سيتم بعد ذلك تمريرها إلى componentDidUpdate. هذه الطريقة ضرورية لإجراء حساب مسبق لبيانات معينة على أساسها يمكنك بعد ذلك ، على سبيل المثال ، طلب إحضار.
مكافأة السؤال 2.
2,5 ? function Input() { const [text, setText] = useState("World!"); useEffect( () => { let id = setTimeout(() => { setText("Hello " + text); }, 1000); return () => { clearTimeout(id); }; }, [text] ); return ( <input value={text} onChange={e => { setText(e.target.value); }} /> ); } a) "World!" b) "Hello World!" c) "Hello Hello World!" d)
هذه بالفعل مسألة معرفة الميزات الجديدة في رد الفعل ؛ لم يكن في مسابقة لدينا. لنجرب في التعليقات لوصف بالتفصيل تشغيل الكود من السؤال الأخير :)