paint-brush
अपने प्रतिक्रिया कौशल को अधिकतम करें: प्रारंभ से अंत तक एक टू-डू सूची ऐप बनाएं (टाइपस्क्रिप्ट + वाइट के साथ)द्वारा@emotta
10,346 रीडिंग
10,346 रीडिंग

अपने प्रतिक्रिया कौशल को अधिकतम करें: प्रारंभ से अंत तक एक टू-डू सूची ऐप बनाएं (टाइपस्क्रिप्ट + वाइट के साथ)

द्वारा Eduardo Motta de Moraes26m2023/02/02
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

इस ब्लॉग पोस्ट में हम एक बुनियादी रिएक्ट एप्लिकेशन को समझने और बनाने के लिए आवश्यक हर चीज से गुजरने वाले हैं। चाहे आप अभी-अभी रिएक्ट के साथ शुरुआत कर रहे हैं या एक अनुभवी डेवलपर हैं जो अपने कौशल में सुधार करना चाहते हैं, यह मार्गदर्शिका आपके लिए है।
featured image - अपने प्रतिक्रिया कौशल को अधिकतम करें: प्रारंभ से अंत तक एक टू-डू सूची ऐप बनाएं (टाइपस्क्रिप्ट + वाइट के साथ)
Eduardo Motta de Moraes HackerNoon profile picture
0-item

इस ब्लॉग पोस्ट में हम एक बुनियादी रिएक्ट एप्लिकेशन को समझने और बनाने के लिए आवश्यक सभी चीजों से गुजरेंगे। चाहे आप अभी-अभी रिएक्ट के साथ शुरुआत कर रहे हैं या एक अनुभवी डेवलपर हैं जो अपने कौशल में सुधार करना चाहते हैं, यह मार्गदर्शिका आपके लिए है।


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


इस गाइड के अंत तक, हमें इस बात की ठोस समझ होगी कि रिएक्ट ऐप को स्क्रैच से कैसे बनाया जाए, और आप अपने खुद के रिएक्ट प्रोजेक्ट बनाने के लिए अपना नया ज्ञान लेने में सक्षम होंगे।

तो चलो शुरू हो जाओ!


*आप हमारे द्वारा बनाए जा रहे ऐप का कोड यहां और लाइव संस्करण यहां देख सकते हैं।

एक संक्षिप्त परिचय

हम कोड लिखने के लिए टाइपस्क्रिप्ट का उपयोग करेंगे और ऐप विकसित करने और बनाने के लिए Vite का उपयोग करेंगे।

टाइपप्रति

टाइपस्क्रिप्ट एक दृढ़ता से टाइप की जाने वाली प्रोग्रामिंग भाषा है जो जावास्क्रिप्ट पर बनती है। व्यावहारिक रूप से, यदि आप पहले से ही जावास्क्रिप्ट जानते हैं, तो आपको टाइपस्क्रिप्ट का उपयोग करने के लिए सीखने की आवश्यकता है कि प्रकार और इंटरफेस का उपयोग कैसे करें।


प्रकार और इंटरफेस हमें कोड में उपयोग किए जा रहे डेटा प्रकारों को परिभाषित करने की अनुमति देते हैं। इससे हम बग को जल्दी पकड़ सकते हैं और लाइन के नीचे की समस्याओं से बच सकते हैं।


उदाहरण के लिए, यदि कोई फ़ंक्शन एक number लेता है लेकिन हम इसे एक string पास करते हैं, तो टाइपस्क्रिप्ट तुरंत शिकायत करेगा:


 const someFunc = (parameter: number) => {...}; someFunc('1') // Argument of type 'string' is not assignable to parameter of type 'number'.


अगर हम जावास्क्रिप्ट का उपयोग कर रहे थे तो हम बाद में केवल बग पकड़ लेंगे।


हमें हमेशा प्रकार निर्दिष्ट करने की आवश्यकता नहीं होती है, क्योंकि टाइपस्क्रिप्ट स्वचालित रूप से अधिक बार उनका अनुमान लगा सकता है।


आप यहां टाइपस्क्रिप्ट की मूल बातें सीख सकते हैं। (या केवल प्रकारों को अनदेखा करें।)

विटे

रिएक्ट एप्लिकेशन को स्पिन करने का सबसे आम तरीका शायद क्रिएट-रिएक्शन-ऐप का उपयोग करना है। हम इसके बजाय Vite ("वीट" की तरह उच्चारित) का उपयोग करेंगे। लेकिन झल्लाहट नहीं, यह उतना ही सरल है - लेकिन अधिक कुशल।


वेबपैक जैसे टूल (हुड के तहत क्रिएट-रिएक्शन-ऐप द्वारा उपयोग किया जाता है) के साथ, आपके पूरे एप्लिकेशन को एक फ़ाइल में बंडल करने की आवश्यकता होती है, इससे पहले कि इसे ब्राउज़र में परोसा जा सके। दूसरी ओर, Vite ब्राउज़र में देशी ES मॉड्यूल का लाभ उठाता है ताकि रोलअप के साथ बंडलिंग को और अधिक कुशल बनाया जा सके, और आवश्यकतानुसार स्रोत कोड के कुछ हिस्सों की सेवा की जा सके।


Vite हॉट मॉड्यूल रिप्लेसमेंट के साथ विकास के समय को भी बहुत तेज कर सकता है - जिसका अर्थ है कि जब भी स्रोत कोड में परिवर्तन किए जाते हैं, तो पूरे एप्लिकेशन के बजाय केवल परिवर्तन ही अपडेट किए जाते हैं।


इसके अलावा, Vite टाइपस्क्रिप्ट, JSX और TSX, CSS और अन्य के लिए मूल समर्थन प्रदान करता है।


इसी तरह क्रिएट-रिएक्शन-ऐप के लिए, वाइट क्रिएट-वाइट नामक एक टूल प्रदान करता है, जो हमें वैनिला जेएस के विकल्पों सहित, या रिएक्ट.


स्पष्ट होने के लिए, हमें रिएक्ट एप्लिकेशन बनाने के लिए वाइट या क्रिएट-रिएक्शन-ऐप जैसे टूल की आवश्यकता नहीं है, लेकिन वे प्रोजेक्ट की स्थापना, कोड को बंडल करने, ट्रांसपिलर का उपयोग करने और बहुत कुछ करके हमारे जीवन को आसान बनाते हैं।

प्रतिक्रिया में गोता लगाना

जेएसएक्स / टीएसएक्स

रिएक्ट हमें कोड में सीधे मार्कअप जोड़ने की अनुमति देता है जिसे बाद में सादे जावास्क्रिप्ट में संकलित किया जाएगा। इसे जेएसएक्स कहा जाता है। जब हम JSX का उपयोग कर रहे होते हैं तो हम अपनी फाइलों को JavaScript के लिए .jsx या TypeScript के लिए .tsx के रूप में सहेज सकते हैं।


यह इस तरह दिख रहा है:


 const element = <h1>Hello, world!</h1>;


यह HTML के समान है, लेकिन यह JavaScript फ़ाइल में सन्निहित है, और यह हमें प्रोग्रामिंग लॉजिक के साथ मार्कअप में हेरफेर करने की अनुमति देता है। हम JSX के अंदर JavaScript कोड भी जोड़ सकते हैं, जब तक कि यह कर्ली ब्रैकेट्स के अंदर है।


उदाहरण के लिए, यदि हमारे पास पाठ की एक सरणी है जिसे हम विभिन्न अनुच्छेद तत्वों के रूप में प्रस्तुत करना चाहते हैं, तो हम ऐसा कर सकते हैं:


 const paragraphs = ["First", "Second", "Third"]; paragraphs.map((paragraph) => <p>{paragraph}</p>);


और इसे कुछ इस तरह संकलित किया जाएगा:


 <p>First</p> <p>Second</p> <p>Third</p>


लेकिन अगर हम ऐसा करने की कोशिश करेंगे तो यह काम नहीं करेगा। ऐसा इसलिए है क्योंकि रिएक्ट घटकों के साथ काम करता है, और JSX को इन घटकों के अंदर प्रस्तुत करने की आवश्यकता होती है।

प्रतिक्रियाशील घटक

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


एक घटक को एक फ़ंक्शन द्वारा परिभाषित किया जाता है जो जेएसएक्स लौटाएगा जिसे ब्राउज़र द्वारा संकलित और प्रस्तुत किया जाएगा। इसलिए उपरोक्त उदाहरण का विस्तार करने के लिए, यदि हम पैराग्राफ तत्वों को रेंडर करना चाहते हैं, तो यह कुछ इस तरह दिखाई देगा:


 // Define the component const Component = () => { const paragraphs = ["First", "Second", "Third"]; return ( <> {paragraphs.map((paragraph) => ( <p>{paragraph}</p> ))} </> ); }; // Use the component in the same way you use an HTML element in the JSX const OtherComponent = () => { return <Component />; };

रंगमंच की सामग्री

अब शायद हम इस घटक को अलग-अलग सूचनाओं के साथ पुन: उपयोग करना चाहते हैं। हम प्रॉप्स का उपयोग करके ऐसा कर सकते हैं - जो कि कुछ डेटा रखने वाली एक जावास्क्रिप्ट वस्तु है।


हमारे उदाहरण में, सरणी को हार्डकोड करने के बजाय, हम इसे घटक को पास कर सकते हैं। नतीजा वही होगा, लेकिन अब घटक पुन: प्रयोज्य होगा।


यदि हम टाइपस्क्रिप्ट का उपयोग कर रहे हैं, तो हमें प्रॉप्स ऑब्जेक्ट के अंदर डेटा के प्रकार निर्दिष्ट करने की आवश्यकता है (वे क्या हैं, इसके लिए कोई संदर्भ नहीं है, इसलिए टाइपस्क्रिप्ट उन्हें अनुमान नहीं लगा सकता है), जो इस मामले में स्ट्रिंग्स की एक सरणी है ( string[] ).


 const Component = (props: { paragraphs: string[] }) => { <> {props.paragraphs.map((paragraph) => ( <p>{paragraph}</p> ))} </>; }; const OtherComponent = () => { const paragraphs = ["First", "Second", "Third"]; return <Component paragraphs={paragraphs} />; };

राज्य

यदि हम एक इंटरएक्टिव घटक बनाना चाहते हैं, तो हमें घटक की स्थिति में जानकारी संग्रहीत करने की आवश्यकता है, ताकि यह इसे "याद" रख सके।


उदाहरण के लिए, यदि हम एक साधारण काउंटर को परिभाषित करना चाहते हैं जो दिखाता है कि बटन कितनी बार क्लिक किया गया है, तो हमें इस मान को संग्रहीत करने और अपडेट करने का एक तरीका चाहिए। रिएक्ट हमें इसे यूजस्टेट हुक के साथ करने देता है ( हुक एक ऐसा फंक्शन है जो आपको रिएक्ट स्टेट और जीवनचक्र सुविधाओं में "हुक" करने देता है)।


हम प्रारंभिक मूल्य के साथ useState हुक कहते हैं, और यह हमें मूल्य के साथ एक सरणी देता है और इसे अपडेट करने के लिए एक फ़ंक्शन देता है।


 import { useState } from "react"; const Counter = () => { const [count, setCount] = useState(0); return ( <> <span>{count}</span> <button onClick={() => setCount(count + 1)}>Increment count</button> </> ); };


इस ज्ञान के साथ, अब हम अपना रिएक्ट ऐप बनाना शुरू करने के लिए तैयार हैं।

प्रोजेक्ट बनाना

निर्भरता

Vite का उपयोग करने के लिए हमें **नोड **और एक पैकेज मैनेजर की आवश्यकता होगी।


नोड स्थापित करने के लिए अपने सिस्टम और कॉन्फ़िगरेशन के आधार पर यहां केवल एक विकल्प चुनें। यदि आप Linux या Mac का उपयोग कर रहे हैं, तो आप इसे Homebrew का उपयोग करके भी इंस्टॉल कर सकते हैं।


पैकेज मैनेजर एनपीएम या यार्न हो सकता है। इस पोस्ट में हम npm का उपयोग करने जा रहे हैं।

प्रोजेक्ट बनाना

अगला यह प्रोजेक्ट बनाने का समय है। टर्मिनल में, हम उस डायरेक्टरी में नेविगेट करते हैं जहाँ प्रोजेक्ट बनाया जाएगा, फिर create-vite कमांड चलाएँ।


 $ npm create vite@latest


हमें अतिरिक्त पैकेज स्थापित करने के लिए कहा जा सकता है (जैसे क्रिएट-वाइट)। y टाइप करें और जारी रखने के लिए एंटर दबाएं।


 Need to install the following packages: [email protected] Ok to proceed? (y)


आगे हमें परियोजना की जानकारी दर्ज करने के लिए कहा जाएगा।


प्रोजेक्ट का नाम दर्ज करें। मैंने my-react-project चुना है।


 ? Project name: › my-react-project


"ढांचे" के रूप में प्रतिक्रिया का चयन करें।


रिएक्ट तकनीकी रूप से एक लाइब्रेरी है न कि एक फ्रेमवर्क , लेकिन इसके बारे में चिंता न करें।


 ? Select a framework: › - Use arrow-keys. Return to submit. Vanilla Vue ❯ React Preact Lit Svelte Others


प्रकार के रूप में टाइपस्क्रिप्ट + एसडब्ल्यूसी का चयन करें।


SWC (स्पीडी वेब कंपाइलर के लिए खड़ा है) रस्ट में लिखा गया एक सुपर-फास्ट टाइपस्क्रिप्ट / जावास्क्रिप्ट कंपाइलर है। वे दावा करते हैं कि "एक धागे पर बेबेल की तुलना में 20 गुना तेज और चार कोर पर 70 गुना तेज"।


 ? Select a variant: › - Use arrow-keys. Return to submit. JavaScript TypeScript JavaScript + SWC ❯ TypeScript + SWC


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


 cd my-react-project npm install npm run dev


कुछ सेकंड के बाद, हम ऐसा कुछ देखेंगे:


 VITE v4.0.4 ready in 486 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h to show help


यदि हम अपना ब्राउज़र खोलते हैं और नेविगेट करते हैं http://localhost:5173 / हम डिफ़ॉल्ट Vite + रिएक्ट पेज देखेंगे:



इसका मतलब है कि सब कुछ वैसा ही है जैसा होना चाहिए और हम अपने ऐप पर काम करना शुरू कर सकते हैं।

ऐप का निर्माण

फ़ाइल संरचना और प्रारंभिक सेटअप

यदि हम अपने कोड संपादक या पसंद के IDE में प्रोजेक्ट खोलते हैं, तो हमें इस तरह की फ़ाइल संरचना देखनी चाहिए:



हम बॉयलरप्लेट फ़ाइलों में से कुछ को हटा सकते हैं, क्योंकि हम उनका उपयोग नहीं करेंगे (सभी .svg और .css फ़ाइलें)।

ऐप फ़ंक्शन में कोड हमें इसके साथ छोड़ने के लिए हटाया जा सकता है:


 function App() { return ( ) } export default App


हम बाद में इस फाइल पर वापस आएंगे।

स्टाइल

स्टाइलिंग यहां फोकस नहीं है, लेकिन हम टेलविंड सीएसएस का उपयोग करेंगे, जो एक लाइब्रेरी है जो हमें HTML तत्वों को क्लास जोड़कर स्टाइल करने देती है। अपने स्वयं के प्रोजेक्ट में दिखाई देने वाली शैलियों को देखने के लिए इन निर्देशों का पालन करें।


अन्यथा आप कोड में कक्षाओं को अनदेखा कर सकते हैं।

डिजाइन के बारे में सोच: घटक लेआउट

डिजाइन प्रक्रिया एक ऐप के विकास का एक अभिन्न अंग है और इसे अनदेखा नहीं किया जाना चाहिए।

हमारे टू-डू लिस्ट ऐप को बनाने के लिए, हमें पहले कंपोनेंट लेआउट के बारे में सोचना होगा।


हम एक बुनियादी यूआई का मज़ाक उड़ाते हैं और इसमें शामिल घटकों के एक पदानुक्रम की रूपरेखा तैयार करते हैं।


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


आदर्श रूप से, एकल-जिम्मेदारी सिद्धांत का पालन करते हुए, हमारे घटकों को केवल एक चीज के लिए जिम्मेदार होना चाहिए।


नीचे दी गई छवि में, बैंगनी रंग के नाम वे घटक हैं जिन्हें हम बनाने जा रहे हैं - बाकी सब मूल HTML तत्व हैं। अगर वे एक-दूसरे के अंदर हैं, तो इसका मतलब है कि माता-पिता-बच्चे का रिश्ता होने की संभावना है।

सहारा: एक स्थिर संस्करण का निर्माण

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


आप इस GitHub रिपॉजिटरी में स्थिर संस्करण के लिए कोड "स्थैतिक-संस्करण" शाखा में पा सकते हैं। पूरी तरह से काम करने वाले ऐप का कोड मुख्य शाखा है।


पात्र


जैसा कि ऊपर बताया गया है, हमारे पास एक ऐसा कंटेनर होगा जो ऐप के प्रत्येक अनुभाग के लिए पुन: उपयोग किया जाएगा। यह कंटेनर विभिन्न तत्वों को बनाने के तरीकों में से एक दिखाता है: उन्हें बच्चों के रूप में पास करके।


 // src/components/Container.tsx const Container = ({ children, title, }: { children: JSX.Element | JSX.Element[]; title?: string; }) => { return ( <div className="bg-green-600 p-4 border shadow rounded-md"> {title && <h2 className="text-xl pb-2 text-white">{title}</h2>} <div>{children}</div> </div> ); }; export default Container;


यह JSX.Element | JSX.Element[] प्रकार के children पैरामीटर के साथ एक प्रॉप्स ऑब्जेक्ट लेता है JSX.Element | JSX.Element[] . इसका मतलब है कि हम इसे किसी अन्य HTML तत्व या हमारे द्वारा बनाए गए किसी अन्य घटक के साथ बना सकते हैं। कंटेनर के अंदर हम जहां चाहें इसे रेंडर कर सकते हैं - इस मामले में दूसरे डिव के अंदर।


हमारे ऐप में, जब हम उन्हें ऐप कंपोनेंट के अंदर इस्तेमाल करते हैं तो यह प्रत्येक सेक्शन (नीचे परिभाषित) को रेंडर करने जा रहा है।


कंटेनर title नाम का एक वैकल्पिक string प्रोप भी लेता है, जो कि जब भी मौजूद होगा, एच 2 के अंदर प्रस्तुत किया जाएगा।


 // src/App.tsx import Container from "./components/Container"; import Input from "./components/Input"; import Summary from "./components/Summary/Summary"; import Tasks from "./components/Tasks/Tasks"; function App() { return ( <div className="flex justify-center m-5"> <div className="flex flex-col items-center"> <div className="sm:w-[640px] border shadow p-10 flex flex-col gap-10"> <Container title={"Summary"}> <Summary /> </Container> <Container> <Input /> </Container> <Container title={"Tasks"}> <Tasks /> </Container> </div> </div> </div> ); } export default App;


सारांश


पहला खंड एक सारांश (सारांश घटक) है जिसमें तीन आइटम (सारांशआइटेम) दिखाए गए हैं: कार्यों की कुल संख्या, लंबित कार्यों की संख्या और पूर्ण किए गए कार्यों की संख्या। यह कंपोनेंट बनाने का एक और तरीका है: बस उन्हें दूसरे कंपोनेंट के रिटर्न स्टेटमेंट में इस्तेमाल करें।


(यह महत्वपूर्ण है कि किसी घटक को किसी अन्य घटक के अंदर कभी भी परिभाषित न किया जाए, क्योंकि इससे अनावश्यक पुनर्रक्षण और बग हो सकते हैं।)


अभी के लिए हम केवल दो घटकों में स्थैतिक डेटा का उपयोग कर सकते हैं।


 // src/components/Summary/SummaryItem.tsx const SummaryItem = ({ itemName, itemValue, }: { itemName: string; itemValue: number; }) => { return ( <article className="bg-green-50 w-36 rounded-sm flex justify-between p-2"> <h3 className="font-bold">{itemName}</h3> <span className="bg-green-900 text-white px-2 rounded-sm"> {itemValue} </span> </article> ); }; export default SummaryItem; // src/components/Summary/Summary.tsx import SummaryItem from "./SummaryItem"; const Summary = () => { return ( <> <div className="flex justify-between"> <SummaryItem itemName={"Total"} itemValue={3} /> <SummaryItem itemName={"To do"} itemValue={2} /> <SummaryItem itemName={"Done"} itemValue={1} /> </div> </> ); }; export default Summary;


आप देखेंगे कि समरीइटम दो प्रॉप्स लेता है: itemName , टाइप स्ट्रिंग का, और itemValue , टाइप number का। ये प्रॉप्स तब पास किए जाते हैं जब समरीइटम कंपोनेंट को समरी कंपोनेंट के अंदर इस्तेमाल किया जाता है, और फिर समरीआइटम JSX में रेंडर किया जाता है।


कार्य


इसी तरह, टास्क सेक्शन (आखिरी वाला) के लिए हमारे पास एक टास्क कंपोनेंट है जो टास्कआइटम कंपोनेंट्स को रेंडर करता है।


साथ ही अभी के लिए स्थिर डेटा के साथ। हमें बाद में टास्कइटम घटक को पुन: प्रयोज्य और गतिशील बनाने के लिए प्रॉप्स के रूप में एक कार्य नाम और एक स्थिति नीचे पारित करने की आवश्यकता होगी।


 // src/components/Tasks/TaskItem.tsx const TaskItem = () => { return ( <div className="flex justify-between bg-white p-1 px-3 rounded-sm"> <div className="flex gap-2 items-center"> <input type="checkbox" /> Task name </div> <button className="bg-green-200 hover:bg-green-300 rounded-lg p-1 px-3"> Delete </button> </div> ); }; export default TaskItem; // src/components/Tasks/Tasks.tsx import TaskItem from "./TaskItem"; const Tasks = () => { return ( <div className="flex flex-col gap-2"> <TaskItem /> </div> ); }; export default Tasks;


इनपुट


अंत में, इनपुट घटक एक लेबल वाला एक फॉर्म है, टाइप टेक्स्ट का एक इनपुट और "कार्य जोड़ें" के लिए एक बटन है। अभी के लिए यह कुछ नहीं करता है, लेकिन हम जल्द ही इसे बदल देंगे।


 // src/components/Input.tsx const InputContainer = () => { return ( <form action="" className="flex flex-col gap-4"> <div className="flex flex-col"> <label className="text-white">Enter your next task:</label> <input className="p-1 rounded-sm" /> </div> <button type="button" className="bg-green-100 rounded-lg hover:bg-green-200 p-1" > Add task </button> </form> ); }; export default InputContainer;

राज्य: अन्तरक्रियाशीलता जोड़ना

प्रतिक्रिया में अन्तरक्रियाशीलता जोड़ने के लिए, हमें घटक की स्थिति में जानकारी संग्रहीत करने की आवश्यकता है।


लेकिन ऐसा करने से पहले, हमें यह सोचने की जरूरत है कि हम समय के साथ डेटा को कैसे बदलना चाहते हैं। हमें इस डेटा के न्यूनतम प्रतिनिधित्व की पहचान करने की आवश्यकता है, और यह पहचानने की आवश्यकता है कि हमें इस स्थिति को संग्रहीत करने के लिए किन घटकों का उपयोग करना चाहिए।


राज्य का न्यूनतम प्रतिनिधित्व


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


हमारे उदाहरण में हम सोच सकते हैं कि हमें कुल कार्यों, लंबित कार्यों और किए गए कार्यों के मूल्यों को ट्रैक करने की आवश्यकता है।


लेकिन कार्यों को ट्रैक करने के लिए, प्रत्येक कार्य और उसकी स्थिति (लंबित या पूर्ण) का प्रतिनिधित्व करने वाली वस्तुओं के साथ एक एकल सरणी होना पर्याप्त है।


 const tasks = [ { name: "task one", done: false, }, { name: "task two", done: true, }, ];


इस डेटा के साथ हम हमेशा वे सभी अन्य जानकारी पा सकते हैं जिनकी हमें सरणी विधियों का उपयोग करके समय प्रस्तुत करने की आवश्यकता होती है। हम विरोधाभासों की संभावना से भी बचते हैं, - जैसे कि कुल 4 कार्य हैं, लेकिन केवल 1 लंबित है और 1 कार्य किया गया है, उदाहरण के लिए।


हमें अपने रूप में (इनपुट घटक में) राज्य की भी आवश्यकता है ताकि हम इसे संवादात्मक बना सकें।


जहां राज्य रहना चाहिए


इसके बारे में इस तरह से सोचें: हम किस राज्य में स्टोर करने जा रहे डेटा तक पहुंचने के लिए कौन से घटकों की आवश्यकता है? यदि यह एकल घटक है, तो राज्य इस घटक में ही रह सकता है। यदि यह एक से अधिक घटक हैं जिन्हें डेटा की आवश्यकता है, तो आपको इन घटकों के सामान्य माता-पिता को ढूंढना चाहिए।


हमारे उदाहरण में, इनपुट घटक को नियंत्रित करने के लिए आवश्यक स्थिति को केवल वहां एक्सेस करने की आवश्यकता है, इसलिए यह इस घटक के लिए स्थानीय हो सकता है।


 // src/components/Input.tsx import { useState } from "react"; const InputContainer = () => { const [newTask, setNewTask] = useState(""); // Initialize newTask and setNewTask return ( <form action="" className="flex flex-col gap-4"> <div className="flex flex-col"> <label className="text-white">Enter your next task:</label> <input className="p-1 rounded-sm" type="text" value={newTask} // Set the input value to newTask onChange={(e) => setNewTask(e.target.value)} // Set newTask to the input value whenever the user types something /> </div> <button type="submit" className="bg-green-100 rounded-lg hover:bg-green-200 p-1" > Add task </button> </form> ); }; export default InputContainer;


यह जो कर रहा है वह इनपुट में हमारे newTask मान को प्रदर्शित कर रहा है, और जब भी इनपुट बदलता है (यानी, जब उपयोगकर्ता कुछ टाइप करता है) तो setNewTask फ़ंक्शन को कॉल करता है।


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


हालाँकि, कार्यों को ट्रैक करने के लिए राज्य को अलग तरह से संभालना होगा, क्योंकि इसे सारांश आइटम घटकों में एक्सेस करने की आवश्यकता है (हमें कुल, लंबित और किए गए कार्यों की संख्या दिखाने की आवश्यकता है) साथ ही टास्कइटम घटकों में (हमें इसकी आवश्यकता है) कार्यों को स्वयं प्रदर्शित करने के लिए)। और यह एक ही स्थिति में होना चाहिए क्योंकि यह जानकारी हमेशा सिंक में होनी चाहिए।


आइए हमारे कंपोनेंट ट्री पर एक नज़र डालें (आप इसके लिए रिएक्ट देव टूल्स का उपयोग कर सकते हैं)।



हम देख सकते हैं कि पहला सामान्य मूल घटक ऐप है। तो यह वह जगह है जहाँ कार्यों के लिए हमारा राज्य रहने वाला है।


राज्य के साथ, जो कुछ बचा होगा, वह डेटा को उन घटकों के लिए प्रॉपर के रूप में पास करना होगा, जिन्हें इसका उपयोग करने की आवश्यकता है।


(हम अभी इस बारे में चिंतित नहीं हैं कि पैरेंट स्थिति में कोई परिवर्तन कैसे करें और कैसे जारी रखें, यह अगला भाग है।)


 // src/App.tsx import { useState } from "react"; import { v4 as uuidv4 } from "uuid"; import Container from "./components/Container"; import Input from "./components/Input"; import Summary from "./components/Summary/Summary"; import Tasks from "./components/Tasks/Tasks"; export interface Task { name: string; done: boolean; id: string; } const initialTasks = [ { name: "task one", done: false, id: uuidv4(), }, { name: "task two", done: true, id: uuidv4(), }, ]; function App() { const [tasks, setTasks] = useState<Task[]>(initialTasks); return ( <div className="flex justify-center m-5"> <div className="flex flex-col items-center"> <div className="border shadow p-10 flex flex-col gap-10 sm:w-[640px]"> <Container title={"Summary"}> <Summary tasks={tasks} /> </Container> <Container> <Input /> </Container> <Container title={"Tasks"}> <Tasks tasks={tasks} /> </Container> </div> </div> </div> ); } export default App;


यहां हम डमी डेटा ( initialTasks कार्य) के साथ कार्यों के मूल्य को आरंभ कर रहे हैं, ताकि हम ऐप समाप्त होने से पहले इसे देख सकें। बाद में हम इसे एक खाली सरणी में बदल सकते हैं, इसलिए नए उपयोगकर्ता को ऐप को नए सिरे से खोलने पर कोई कार्य नहीं दिखाई देगा।


name और done गुणों के अलावा, हम अपने कार्य ऑब्जेक्ट में एक आईडी भी जोड़ रहे हैं, क्योंकि शीघ्र ही इसकी आवश्यकता होगी।


हम टास्क ऑब्जेक्ट्स में वैल्यू के प्रकारों के साथ एक interface को परिभाषित कर रहे हैं, और इसे useState फंक्शन में पास कर रहे हैं। इस मामले में यह आवश्यक है, क्योंकि जब हम tasks के प्रारंभिक मूल्य को एक खाली सरणी में बदलते हैं, या जब हम इसे प्रॉपर के रूप में पास करते हैं, तो टाइपस्क्रिप्ट इसका अनुमान नहीं लगा पाएगा।


अंत में, ध्यान दें कि हम कार्यों को सारांश और कार्य घटकों के आधार के रूप में पास कर रहे हैं। इसे समायोजित करने के लिए इन घटकों को बदलना होगा।


 // src/components/Summary/Summary.tsx import { Task } from "../../App"; import SummaryItem from "./SummaryItem"; const Summary = ({ tasks }: { tasks: Task[] }) => { const total = tasks.length; const pending = tasks.filter((t) => t.done === false).length; const done = tasks.filter((t) => t.done === true).length; return ( <> <div className="flex flex-col gap-1 sm:flex-row sm:justify-between"> <SummaryItem itemName={"Total"} itemValue={total} /> <SummaryItem itemName={"To do"} itemValue={pending} /> <SummaryItem itemName={"Done"} itemValue={done} /> </div> </> ); }; export default Summary;


हमने सारांश घटक को अपडेट किया है ताकि यह अब tasks को एक सहारा के रूप में स्वीकार करे। हमने total मूल्य, pending और पूर्ण को भी परिभाषित done , जो हमारे पास पहले मौजूद स्थिर itemValue के स्थान पर सारांशइटम घटकों के प्रॉप के रूप में पारित किया जाएगा।


 // src/components/Tasks/Tasks.tsx import { Task } from "../../App"; import TaskItem from "./TaskItem"; const Tasks = ({ tasks }: { tasks: Task[] }) => { return ( <div className="flex flex-col gap-2"> {tasks.map((t) => ( <TaskItem key={t.id} name={t.name} /> ))} </div> ); }; export default Tasks; // src/components/Tasks/TaskItem.tsx import { useState } from "react"; const TaskItem = ({ name }: { name: string }) => { const [done, setDone] = useState(false); return ( <div className="flex justify-between bg-white p-1 px-3 rounded-sm gap-4"> <div className="flex gap-2 items-center"> <input type="checkbox" checked={done} onChange={() => setDone(!done)} /> {name} </div> <button className="bg-green-200 hover:bg-green-300 rounded-lg p-1 px-3"> Delete </button> </div> ); }; export default TaskItem;


कार्य घटक के लिए, हम task को एक प्रोप के रूप में भी लेते हैं, और इसके name की संपत्ति को टास्कइटम घटकों में मैप करते हैं। परिणामस्वरूप, हमें tasks ऐरे के अंदर प्रत्येक ऑब्जेक्ट के लिए एक टास्कआइटम घटक मिलता है। हम name को प्रोप के रूप में स्वीकार करने के लिए टास्कआइटम घटक को भी अपडेट करते हैं।


यह वह जगह है जहाँ आईडी काम आती है, क्योंकि हमें हर बार एक अद्वितीय कुंजी पास करने की आवश्यकता होती है जब हमारे पास बाल घटकों की एक सूची होती है। यदि हम कुंजी नहीं जोड़ते हैं, तो इससे पुनः रेंडर करने में बग हो सकते हैं। (प्रोडक्शन ऐप में, आईडी बैकएंड से आने की संभावना है।)


अभी के लिए परिणाम यह है:



हम अपने डमी डेटा को दर्शाते हुए सारांश संख्या और कार्य नाम पहले से ही देख सकते हैं। लेकिन हमारे पास अभी भी कार्यों को जोड़ने या हटाने का कोई तरीका नहीं है।


उलटा डेटा प्रवाह जोड़ना


अपने ऐप को पूरा करने के लिए, हमें इनपुट और टास्कइटम चाइल्ड कंपोनेंट्स से ऐप कंपोनेंट स्टेट (जहां टास्क डेटा है) को बदलने का एक तरीका चाहिए।


ऐसा करने के लिए, हम इवेंट हैंडलर को परिभाषित करने के लिए useState हुक द्वारा उत्पन्न फ़ंक्शंस का उपयोग कर सकते हैं और उन्हें प्रॉपर के रूप में पास कर सकते हैं। एक बार जब हम ऐसा कर लेते हैं, तो हम उन्हें चाइल्ड कंपोनेंट से उचित उपयोगकर्ता इंटरैक्शन के दौरान बस कॉल कर देते हैं।


सुनिश्चित करें कि जब भी आप इसे अपडेट कर रहे हों तो कभी भी स्टेट को म्यूट न करें, क्योंकि इससे बग्स पैदा होंगे। इसे अद्यतन करते समय हमेशा राज्य वस्तु को एक नए से बदलें।


नीचे हमारा अंतिम ऐप कंपोनेंट है जिसमें हैंडलर घोषित किए गए हैं और इनपुट और टास्क कंपोनेंट्स के प्रॉप्स के रूप में पास किए गए हैं।


handleSubmit पुराने कार्यों के साथ नए के साथ एक नई सरणी देता है। toggleDoneTask निर्दिष्ट id के लिए, विपरीत done संपत्ति के साथ एक नई सरणी देता है। handleDeleteTask निर्दिष्ट id के साथ कार्य के बिना एक नई सरणी देता है।


 // src/App.tsx import { FormEvent, useState } from "react"; import { v4 as uuidv4 } from "uuid"; import Container from "./components/Container"; import Input from "./components/Input"; import Summary from "./components/Summary/Summary"; import Tasks from "./components/Tasks/Tasks"; export interface Task { name: string; done: boolean; id: string; } function App() { const [tasks, setTasks] = useState<Task[]>([]); const handleSubmit = (e: FormEvent<HTMLFormElement>, value: string) => { e.preventDefault(); const newTask = { name: value, done: false, id: uuidv4(), }; setTasks((tasks) => [...tasks, newTask]); }; const toggleDoneTask = (id: string, done: boolean) => { setTasks((tasks) => tasks.map((t) => { if (t.id === id) { t.done = done; } return t; }) ); }; const handleDeleteTask = (id: string) => { setTasks((tasks) => tasks.filter((t) => t.id !== id)); }; return ( <div className="flex justify-center m-5"> <div className="flex flex-col items-center"> <div className="border shadow p-10 flex flex-col gap-10 sm:w-[640px]"> <Container title={"Summary"}> <Summary tasks={tasks} /> </Container> <Container> <Input handleSubmit={handleSubmit} /> </Container> <Container title={"Tasks"}> <Tasks tasks={tasks} toggleDone={toggleDoneTask} handleDelete={handleDeleteTask} /> </Container> </div> </div> </div> ); } export default App;


ऐप घटक स्थिति को अपडेट करने के लिए handleSubmit का उपयोग करके यह अंतिम इनपुट घटक है।


 // src/components/Input.tsx import { FormEvent, useState } from "react"; const InputContainer = ({ handleSubmit, }: { handleSubmit: (e: FormEvent<HTMLFormElement>, value: string) => void; }) => { const [newTaskName, setNewTaskName] = useState(""); return ( <form action="" className="flex flex-col gap-4" onSubmit={(e) => { handleSubmit(e, newTaskName); setNewTaskName(""); }} > <div className="flex flex-col"> <label className="text-white">Enter your next task:</label> <input className="p-1 rounded-sm" type="text" value={newTaskName} onChange={(e) => setNewTaskName(e.target.value)} /> </div> <button type="submit" className="bg-green-100 rounded-lg hover:bg-green-200 p-1" > Add task </button> </form> ); }; export default InputContainer;


यह अंतिम कार्य घटक है, जिसे हमने प्रॉप्स को ऐप से टास्कइटम तक पास करने के लिए अपडेट किया है। हमने "अभी तक कोई कार्य नहीं!" वापस करने के लिए एक टर्नरी ऑपरेटर भी जोड़ा है। जब कोई कार्य न हो।


 // src/components/Tasks/Tasks.tsx import { Task } from "../../App"; import TaskItem from "./TaskItem"; const Tasks = ({ tasks, toggleDone, handleDelete, }: { tasks: Task[]; toggleDone: (id: string, done: boolean) => void; handleDelete: (id: string) => void; }) => { return ( <div className="flex flex-col gap-2"> {tasks.length ? ( tasks.map((t) => ( <TaskItem key={t.id} name={t.name} done={t.done} id={t.id} toggleDone={toggleDone} handleDelete={handleDelete} /> )) ) : ( <span className="text-green-100">No tasks yet!</span> )} </div> ); }; export default Tasks;


और यह अंतिम टास्कआइटम घटक है, ऐप घटक स्थिति को अपडेट करने के लिए toggleDone और handleDelete का उपयोग कर रहा है।


 // src/components/Tasks/TaskItem.tsx const TaskItem = ({ name, done, id, toggleDone, handleDelete, }: { name: string; done: boolean; id: string; toggleDone: (id: string, done: boolean) => void; handleDelete: (id: string) => void; }) => { return ( <div className="flex justify-between bg-white p-1 px-3 rounded-sm gap-4"> <div className="flex gap-2 items-center"> <input type="checkbox" checked={done} onChange={() => toggleDone(id, !done)} /> {name} </div> <button className="bg-green-200 hover:bg-green-300 rounded-lg p-1 px-3" type="button" onClick={() => handleDelete(id)} > Delete </button> </div> ); }; export default TaskItem;


और यहां कुछ कार्य जोड़ने के बाद हमारा अंतिम ऐप है!



यदि आप साथ में कोडिंग कर रहे हैं, तो आप इन निर्देशों का पालन करके अपना खुद का ऐप परिनियोजित कर सकते हैं।

आप रेपो को उन सभी कोड के साथ पा सकते हैं जिनसे हम यहां गुजरे हैं , और ऐप का लाइव संस्करण यहां

अंतिम शब्द

अंत में, एक टू-डू लिस्ट ऐप बनाना रिएक्ट और उसके सिद्धांतों की हमारी समझ को सीखने और मजबूत करने का एक शानदार तरीका हो सकता है। प्रक्रिया को छोटे चरणों में तोड़कर और सर्वोत्तम प्रथाओं का पालन करके, हम अपेक्षाकृत कम समय में एक कार्यात्मक ऐप बना सकते हैं।


हमने कवर किया है:


  • घटकों, स्थिति और उलटा डेटा प्रवाह की प्रमुख अवधारणाएँ।

  • ऐप का डिज़ाइन और आर्किटेक्चर।

  • एकल-जिम्मेदारी सिद्धांत जैसे सर्वोत्तम अभ्यास


इस गाइड में बताए गए चरणों का पालन करके, अब आपको इस बात की ठोस समझ होनी चाहिए कि एक साधारण रिएक्ट ऐप कैसे बनाया जाए और इसे अपने प्रोजेक्ट पर लागू करने में सक्षम हों।


हैप्पी कोडिंग!