प्रतिक्रियाशील हुक के साथ कार्यात्मक घटक। वे बेहतर क्यों हैं?

अपेक्षाकृत हाल ही में React.js संस्करण 16.8 जारी किया गया, जिसके साथ हुक हमारे लिए उपलब्ध हो गए। हुक की अवधारणा आपको रिएक्ट की सभी विशेषताओं का उपयोग करके पूर्ण-कार्यात्मक कार्यात्मक घटकों को लिखने की अनुमति देती है, और आप कक्षाओं का उपयोग करने की तुलना में कई तरीकों से इसे और अधिक आसानी से करने की अनुमति देते हैं।


कई ने आलोचना के साथ हुक की उपस्थिति को माना, और इस लेख में मैं कुछ महत्वपूर्ण लाभों के बारे में बात करना चाहूंगा जो हुक के साथ कार्यात्मक घटक हमें देते हैं, और हमें उन्हें क्यों स्विच करना चाहिए।


मैं जानबूझकर हुक का उपयोग करने के विवरण में नहीं होगा। इस लेख में उदाहरणों को समझने के लिए यह बहुत महत्वपूर्ण नहीं है; प्रतिक्रिया की एक सामान्य समझ पर्याप्त है। यदि आप इस विषय पर सटीक रूप से पढ़ना चाहते हैं, तो हुक के बारे में जानकारी प्रलेखन में है , और यदि यह विषय दिलचस्प है, तो मैं हुक के बारे में और अधिक विस्तार से एक लेख लिखूंगा कि कौन सा, और कैसे सही तरीके से उपयोग किया जाए।


हुक कोड पुन: उपयोग को आसान बनाते हैं


आइए एक घटक की कल्पना करें जो एक सरल आकार प्रदान करता है। ऐसा कुछ जो केवल कुछ इनपुट का उत्पादन करेगा और हमें उन्हें संपादित करने की अनुमति देगा।


कुछ इस तरह, अगर बहुत सरलीकृत किया जाता है, तो यह घटक एक वर्ग की तरह दिखेगा:


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

अब कल्पना करें कि जब वे बदलते हैं तो हम स्वचालित रूप से फ़ील्ड मानों को सहेजना चाहते हैं। मैं अतिरिक्त कार्यों की घोषणाओं को छोड़ने का सुझाव देता हूं, जैसे कि shallowEqual और shallowEqual


 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 बदल दिया और useState बचत नहीं करने के लिए मसौदा तैयार करने का कारण बना, लेकिन उपयोग हुक का उपयोग करके घटक प्रदान करने के बाद।


जो अंतर मैं यहां दिखाना चाहता हूं (अन्य हैं, हम नीचे चर्चा करेंगे): हम इस कोड को निकाल सकते हैं और इसे दूसरी जगह उपयोग कर सकते हैं:


 //  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 हुक का उपयोग कर सकते हैं जो हमने सिर्फ अन्य घटकों में लिखा था! यह, ज़ाहिर है, एक बहुत ही सरल उदाहरण है, लेकिन उसी कार्यक्षमता का पुन: उपयोग करना एक बहुत ही उपयोगी विशेषता है।


हुक आपको अधिक सहज कोड लिखने की अनुमति देते हैं।


एक घटक की कल्पना करें (अब, एक वर्ग के रूप में), जो, उदाहरण के लिए, वर्तमान चैट विंडो, संभावित प्राप्तकर्ताओं की सूची और संदेश भेजने के लिए एक फॉर्म प्रदर्शित करता है। कुछ इस तरह:


 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 अलग है, हम एक नया तरीका बना रहे हैं। यह हमें इस बारे में बिल्कुल भी सोचने की अनुमति नहीं देता है कि क्या भविष्य में कुछ बदल जाएगा - हम अभी जो भी है उसके साथ काम कर रहे हैंप्रत्येक रेंडर घटक अपने आप में वह सब कुछ बंद कर देता है जो इससे संबंधित है


हुक हमें जीवन चक्र से बचाते हैं


यह आइटम पिछले एक के साथ ओवरलैप होता है। प्रतिक्रियात्मक रूप से एक इंटरफ़ेस का वर्णन करने के लिए एक पुस्तकालय है। घोषणा करने से घटकों के लेखन और समर्थन में बहुत सुविधा होती है, आप कम सोच सकते हैं कि यदि हमने रिएक्ट का उपयोग नहीं किया है तो अनिवार्य रूप से क्या करना होगा।


इसके बावजूद, कक्षाओं का उपयोग करते समय, हमें घटक जीवन चक्र का सामना करना पड़ता है। यदि आप गहराई में नहीं जाते हैं, तो यह इस तरह दिखता है:


  • घटक माउंट
  • घटक अद्यतन ( state या props बदलते समय)
  • घटक निकालना

यह सुविधाजनक लगता है, लेकिन मुझे विश्वास है कि यह आदत के कारण पूरी तरह से सुविधाजनक है। यह दृष्टिकोण रिएक्ट की तरह नहीं है।


इसके बजाय, हुक के साथ कार्यात्मक घटक हमें घटकों को लिखने की अनुमति देते हैं, जीवन चक्र के बारे में नहीं, बल्कि सिंक्रनाइज़ेशन के बारे में सोचते हैं। हम फ़ंक्शन लिखते हैं ताकि इसका परिणाम विशिष्ट रूप से बाहरी मापदंडों और आंतरिक स्थिति के आधार पर इंटरफ़ेस की स्थिति को दर्शाता है।


useEffect , जो कई componentDidMount रूप में प्रत्यक्ष प्रतिस्थापन के रूप में अनुभव करता है, componentDidUpdate useEffect componentDidUpdate और इसी तरह, वास्तव में दूसरे के लिए अभिप्रेत है। इसका उपयोग करते समय, हम प्रतिक्रिया को बताते हैं: "जब आप इसे प्रस्तुत करते हैं, तो कृपया इन प्रभावों का प्रदर्शन करें।"


यहाँ एक अच्छा उदाहरण है कि कैसे घटक उपयोग के बारे में एक बड़े लेख से क्लिक काउंटर के साथ काम करता है:


  • प्रतिक्रिया: मुझे बताएं कि इस राज्य के साथ क्या प्रस्तुत करना है।
  • आपका घटक:
    • यहाँ रेंडर रिजल्ट है: <p> 0 </p>
    • और कृपया, समाप्त होने पर इस प्रभाव को निष्पादित करें: () => { document.title = ' 0 ' }
  • प्रतिक्रिया: ठीक है। इंटरफ़ेस अद्यतन करना। अरे, ब्राउज़र, मैं डोम को अपडेट कर रहा हूं
  • ब्राउज़र: महान, मैंने आकर्षित किया।
  • प्रतिक्रिया: सुपर, अब मैं उस प्रभाव को कॉल करूंगा जो मुझे घटक से प्राप्त हुआ था।
    • यह शुरू होता है () => { document.title = ' 0 ' }

बहुत अधिक घोषणात्मक, है ना?


परिणाम


रिएक्ट हुक हमें कुछ समस्याओं से छुटकारा पाने और घटकों की समझ और कोडिंग की सुविधा प्रदान करते हैं। आपको केवल उन मानसिक मॉडल को बदलने की आवश्यकता है जो हम उन पर लागू करते हैं। कार्यात्मक घटक अनिवार्य रूप से मापदंडों के इंटरफ़ेस फ़ंक्शन हैं। वे हर चीज का वर्णन करते हैं जैसा कि किसी भी समय होना चाहिए, और परिवर्तनों के बारे में प्रतिक्रिया देने के बारे में सोचने में मदद न करें।


हां, कभी-कभी आपको यह सीखने की आवश्यकता होती है कि उन्हें सही तरीके से कैसे उपयोग किया जाए, लेकिन उसी तरह, हमने यह नहीं सीखा कि कक्षाओं के रूप में घटकों का उपयोग कैसे करें।

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


All Articles