Vue 3 स्टेट मैनेजमेंट एडमिन पैनलों के लिए: Pinia बनाम लोकल
Vue 3 में एडमिन पैनलों के लिए state मैनेजमेंट: असली उदाहरणों (फ़िल्टर, ड्राफ्ट, टैब्स) के साथ Pinia, provide/inject और लोकल state के बीच चुनें।

एडमिन पैनलों में state को जटिल क्या बनाता है
एडमिन पैनल अक्सर state-भारित महसूस होते हैं क्योंकि एक स्क्रीन पर कई चलती हुई चीज़ें होती हैं। एक तालिका सिर्फ डेटा नहीं है। उसमें sorting, फ़िल्टर, pagination, चयनित पंक्तियाँ, और वह "क्या हुआ था?" संदर्भ भी शामिल है जिस पर उपयोगकर्ता भरोसा करते हैं। लंबे फॉर्म, role-based permissions, और ऐसे एक्शन जोड़ें जो UI की अनुमति बदल दें—और छोटी-छोटी state की स्टाइलें महत्त्वपूर्ण हो जाती हैं।
मुद्दा वैल्यूज़ स्टोर करने का नहीं है। चुनौती तब आती है जब कई कंपोनेंट्स को एक ही सत्य चाहिए। अगर कोई फ़िल्टर चिप कहता है "Active" तो तालिका, URL और export एक्शन—तीनों का समझौता एक जैसा होना चाहिए। अगर कोई उपयोगकर्ता रिकॉर्ड संपादित करता है और नेविगेट कर जाता है, तो ऐप चुपचाप उनका काम खोना नहीं चाहिए। अगर वे दो टैब खोलते हैं, तो एक टैब दूसरे को ओवरराइट नहीं करना चाहिए।
Vue 3 में आमतौर पर आप तीन जगहों के बीच चुनते हैं जहाँ state रखी जा सकती है:
- लोकल कंपोनेंट state: एक ही कंपोनेंट का स्वामित्व और अनमाउंट होने पर रिसेट करना सुरक्षित।
provide/inject: पेज या फीचर एरिया तक सीमित साझा state, बिना props ड्रिलिंग के।- Pinia: साझा state जो नेविगेशन के बाद भी बचना चाहिए, रूट्स के पार reuse होना चाहिए, और डिबग करना आसान होना चाहिए।
सोचने का एक उपयोगी तरीका: हर state के टुकड़े के लिए तय करें कि वह कहाँ रहेगा ताकि वह सही रहे, उपयोगकर्ता को आश्चर्य न हो, और स्पैगेटी में न बदल जाए।
नीचे दिए उदाहरण तीन सामान्य एडमिन समस्याओं पर टिके हैं: फ़िल्टर और तालिकाएँ (क्या टिके, क्या रिसेट हो), ड्राफ्ट्स और अनसेव्ड एडिट्स (ऐसे फॉर्म जिन पर लोग भरोसा कर सकें), और मल्टी-टैब एडिटिंग (स्टेट टकराव से बचना)।
चुनने से पहले state को वर्गीकृत करने का सरल तरीका
Tools की बहस आसान तब होती है जब आप पहले यह नाम दें कि आपके पास किस तरह का state है। अलग-अलग state प्रकार अलग तरह व्यवहार करते हैं, और इन्हें मिलाना ही अजीब बग बनाता है।
एक व्यावहारिक विभाजन:
- UI state: toggles, खुले dialogs, चयनित पंक्तियाँ, सक्रिय टैब, sort order।
- Server state: API प्रतिक्रियाएँ, loading flags, errors, आख़िरी रिफ्रेश समय।
- Form state: फ़ील्ड वैल्यूज़, validation errors, dirty flags, अदृश्य ड्राफ्ट्स।
- Cross-screen state: कोई भी चीज़ जिसे कई routes को पढ़ना या बदलना चाहिए (वर्तमान workspace, साझा permissions)।
फिर स्कोप पर निर्णय लें। आज यह state कहाँ इस्तेमाल हो रहा है, यह पूछें—कहीं यह भविष्य में उपयोग होने की जगह नहीं बल्कि आज की ज़रूरत मायने रखती है। अगर यह केवल एक तालिका कंपोनेंट के अंदर मायने रखता है, तो लोकल state अक्सर काफी है। अगर एक पेज पर दो sibling कंपोनेंट्स को इसकी जरूरत है, तो असली समस्या पेज-लेवल शेयरिंग है। अगर कई routes को इसकी ज़रूरत है, तो आप.shared app state के दायरे में हैं।
आगे है लाइफटाइम। कुछ state को ड्रॉअर बंद करने पर रिसेट होना चाहिए। दूसरी चीज़ नेविगेशन के बाद बचनी चाहिए (फ़िल्टर जब आप रिकॉर्ड में क्लिक कर वापस आते हैं)। कुछ रीलोड के बाद बचनी चाहिए (एक लंबा ड्राफ्ट जिसे उपयोगकर्ता बाद में लौटकर पूरा करे)। तीनों को एक जैसा व्यवहार देना ही फ़िल्टर के रहस्यमय रिसेट या ड्राफ्ट्स के गायब होने का कारण बनता है।
अंत में, कनकरेंसी जाँचें। एडमिन पैनल जल्दी किनारे के मामलों से टकराते हैं: उपयोगकर्ता एक ही रिकॉर्ड दो टैब में खोलता है, एक बैकग्राउंड रिफ्रेश किसी पंक्ति को अपडेट कर देता है जबकि एक फॉर्म dirty है, या दो एडिटर्स एक साथ save करने की दौड़ में हों।
उदाहरण: एक “Users” स्क्रीन जिसमें फ़िल्टर, एक तालिका, और एक एडिट ड्रॉअर है। फ़िल्टर UI state हैं जिनका पेज लाइफटाइम है। पंक्तियाँ server state हैं। ड्रॉअर फ़ील्ड form state हैं। अगर वही उपयोगकर्ता दो टैबों में संपादित हो रहा है, तो आपको स्पष्ट concurrency निर्णय लेना होगा: ब्लॉक करें, मर्ज करें, या चेतावनी दें।
एक बार जब आप state को प्रकार, स्कोप, लाइफटाइम, और concurrency के अनुसार लेबल कर सकें, तो टूल का चुनाव आमतौर पर स्पष्ट हो जाता है (लोकल, provide/inject, या Pinia)।
कैसे चुनें: एक निर्णय प्रक्रिया जो टिकती है
अच्छे state चुनाव एक आदत से शुरू होते हैं: किसी टूल को चुनने से पहले state को सादे शब्दों में वर्णित करें। एडमिन पैनल तालिकाएँ, फ़िल्टर, बड़े फॉर्म, और रिकॉर्ड्स के बीच नेविगेशन मिलाते हैं, इसलिए छोटा सा state भी बग का चुम्बक बन सकता है।
5-स्टेप निर्णय प्रक्रिया
-
किसे इस state की आवश्यकता है?
- एक कंपोनेंट: इसे लोकल रखें।
- एक पेज के अंतर्गत कई कंपोनेंट्स:
provide/injectपर विचार करें। - कई रूट्स: Pinia पर विचार करें।
फ़िल्टर इसका अच्छा उदाहरण हैं। यदि वे केवल उस तालिका को प्रभावित करते हैं जो उन्हें own करता है, तो लोकल state ठीक है। यदि फ़िल्टर हेडर में हैं लेकिन नीचे की तालिका उन्हें drive करती है, तो पेज-स्कोप्ड शेयरिंग (अक्सर
provide/inject) साफ़ बनाती है। -
कितनी देर तक यह जीवन में रहना चाहिए?
- अगर यह कंपोनेंट के अनमाउंट होते ही गायब हो सकता है, तो लोकल state आदर्श है।
- अगर इसे रूट बदलने पर बचना चाहिए, तो Pinia अक्सर बेहतर फिट है।
- अगर इसे रीलोड पर भी बचाना है, तो persistence (storage) चाहिए, चाहे वह कहीं भी रखा गया हो।
यह ड्राफ्ट्स के लिए सबसे ज़्यादा मायने रखता है। अनसेव्ड एडिट्स भरोसे-संवेदनशील होते हैं: लोग उम्मीद करते हैं कि यदि वे कहीं और क्लिक कर लौटें तो ड्राफ्ट अभी भी मौजूद होगा।
-
क्या इसे ब्राउज़र टैब्स के पार साझा करना चाहिए या हर टैब अलग होना चाहिए?
मल्टी-टैब एडिटिंग वही जगह है जहाँ बग छिपते हैं। यदि हर टैब का अपना ड्राफ्ट होना चाहिए, तो एकल ग्लोबल सिंगलटन से बचें। रिकॉर्ड ID द्वारा की-कीया गया state पसंद करें, या उसे पेज-स्कोप्ड रखें ताकि एक टैब दूसरे को ओवरराइट न कर सके।
-
सबसे सरल विकल्प चुनें जो फिट बैठता हो।
लोकल से शुरू करें। केवल तब ऊपर बढ़ें जब आपको वास्तविक दर्द महसूस हो: prop drilling, डुप्लिकेट लॉजिक, या कठिन-से-रिप्रोड्यूस होने वाले रिसेट्स।
-
अपने डिबगिंग की ज़रूरतों की पुष्टि करें।
अगर आपको स्क्रीन-दर-स्क्रीन बदलावों का स्पष्ट, निरीक्षण योग्य व्यू चाहिए, तो Pinia के केंद्रीकृत actions और state inspection से घंटे बच सकते हैं। अगर state अल्पकालिक और स्पष्ट है, तो लोकल state पढ़ने में आसान है।
लोकल कंपोनेंट state: कब यह काफी है
लोकल state डिफ़ॉल्ट है जब डेटा केवल एक कंपोनेंट पर मायने रखता है और एक पेज पर कहीं और उपयोग नहीं होता। स्टोर ओवरबिल्ड करने से बचें—आमतौर पर आप एक स्टोर बनाएंगे जिसे महीनों तक मेंटेन करना होगा।
एक स्पष्ट फिट एक single table है जिसकी अपनी फ़िल्टर हों। अगर फ़िल्टर केवल उसी तालिका को प्रभावित करते हैं (उदाहरण: Users list) और कुछ और उनकी परवाह नहीं करता, तो उन्हें तालिका कंपोनेंट के अंदर ref के रूप में रखें। वही छोटे UI state पर लागू होता है जैसे “मॉडल खुला है?”, “कौन सी पंक्ति संपादित हो रही है?”, और “फिलहाल कौन-सी आइटम्स चयनित हैं?”।
जो आप compute कर सकते हैं उसे स्टोर न करें। “Active filters (3)” बैज को वर्तमान फ़िल्टर वैल्यूज़ से compute करें। सॉर्ट लेबल, फॉर्मेटेड सारांश, और “सहेज सकते हैं” जैसे फ़्लैग्स भी computed होने चाहिए ताकि वे स्वतः सिंक में रहें।
रीसेट नियम टूल से ज़्यादा मायने रखते हैं। तय करें क्या रूट चेंज पर साफ़ होगा (आम तौर पर सब कुछ), और क्या उसी पेज के अंदर व्यू बदलने पर रखा जाएगा (आप फ़िल्टर रख सकते हैं पर अस्थायी चयन रिसेट कर सकते हैं ताकि अप्रत्याशित बल्क एक्शन न हों)।
लोकल कंपोनेंट state आमतौर पर पर्याप्त है जब:
- राज्य एक widget को प्रभावित करता है (एक फॉर्म, एक तालिका, एक मॉडाल)।
- कोई और स्क्रीन इसे पढ़ने या बदलने की ज़रूरत नहीं है।
- आप इसे 1–2 कंपोनेंट्स के अंदर रख सकते हैं बिना कई लेयर्स से props पास किए।
- आप इसका रिसेट व्यवहार एक वाक्य में बता सकते हैं।
मुख्य सीमा गहराई है। जब आप वही state कई nested कंपोनेंट्स के जरिए पास कर रहे हों, लोकल state prop drilling बन जाता है, और यह आमतौर पर provide/inject या स्टोर की ओर संकेत करता है।
provide/inject: पेज या फीचर एरिया के भीतर state शेयर करना
provide/inject लोकल state और एक फ़ुल स्टोर के बीच बैठता है। एक पैरेंट उन मानों को सबके लिए provide करता है, और nested कंपोनेंट्स उन्हें inject करते हैं बिना props ड्रिलिंग के। एडमिन पैनलों में यह तब अच्छा फिट है जब state एक स्क्रीन या फीचर एरिया की हो, न कि पूरे ऐप की।
एक सामान्य पैटर्न पेज शेल है जो state का मालिक होगा और छोटे कंपोनेंट्स उसे उपयोग करेंगे: एक फ़िल्टर बार, तालिका, बल्क एक्शन टूलबार, एक डिटेल्स ड्रॉअर, और एक "अनसेव्ड चेंजेस" बैनर। शेल एक छोटा reactive सरफेस provide कर सकता है जैसे filters ऑब्जेक्ट, एक draftStatus ऑब्जेक्ट (dirty, saving, error), और कुछ read-only फ़्लैग (उदा. permissions के आधार पर isReadOnly)।
क्या provide करें (इसे छोटा रखें)
अगर आप सब कुछ provide कर देंगे तो आपने असल में बिना स्ट्रक्चर के एक स्टोर बना लिया है। केवल वही provide करें जिसकी कई बच्चों को सचमुच ज़रूरत है। फ़िल्टर एक क्लासिक उदाहरण हैं: जब तालिका, चिप्स, export action, और pagination सबको सिंक में रहना हो तो एक स्रोत साझा करना बेहतर है बनाम props और events को जुगल करना।
स्पष्टता और जोखिम
सबसे बड़ा जोखिम छुपी हुई निर्भरताएँ हैं: एक child "बस काम करता है" क्योंकि कुछ ऊपर से data provide किया गया है, और बाद में यह बताना मुश्किल होता है कि अपडेट कहाँ से आ रहे हैं।
इसे पठनीय और टेस्टेबल रखने के लिए injections को स्पष्ट नाम दें (अक्सर constants या Symbols के साथ)। और mutable ऑब्जेक्ट देने के बजाय actions प्रदान करना बेहतर है। एक छोटा API जैसे setFilter, markDirty, और resetDraft ownership और अनुमति दिए गए परिवर्तन स्पष्ट बनाता है।
Pinia: स्क्रीन-स्तर साझा state और अनुमान्य अपडेट्स
Pinia तब चमकता है जब वही state routes और कंपोनेंट्स के पार लगातार बनी रहनी चाहिए। एडमिन पैनल में इसका अर्थ अक्सर वर्तमान उपयोगकर्ता, उनकी अनुमति क्या हैं, कौन सा संगठन/workspace चुना गया है, और ऐप-स्तर सेटिंग्स होता है। हर स्क्रीन यदि इसे फिर से लागू करने लगे तो यह दर्दनाक हो जाता है।
एक स्टोर उपयोगी है क्योंकि यह पढ़ने और अपडेट करने के लिए एक जगह देता है। कई लेयर के माध्यम से props पास करने के बजाय आप जहां जरूरत हो वहां स्टोर इम्पोर्ट करते हैं। जब आप सूची से डिटेल पेज पर जाते हैं, बाकी UI अभी भी उसी चयनित org, permissions, और सेटिंग्स पर प्रतिक्रिया कर सकती है।
क्यों Pinia में मेंटेन करना आसान लगता है
Pinia एक सरल संरचना को बढ़ावा देता है: raw वैल्यूज़ के लिए state, derived वैल्यूज़ के लिए getters, और अपडेट्स के लिए actions। एडमिन UI में यह संरचना “quick fixes” को बिखरे mutations में बदलने से रोकती है।
अगर canEditUsers वर्तमान role और किसी feature flag पर निर्भर करता है, तो उस नियम को एक getter में रखें। अगर org बदलने पर cached selections साफ़ करनी हैं और नेविगेशन रीलोड करना है, तो उस अनुक्रम को एक action में रखें। इस तरह कम रहस्यमयी watchers और "यह क्यों बदला?" वाले पल रहते हैं।
Pinia Vue DevTools के साथ भी अच्छा काम करता है। जब कोई बग आता है, तो स्टोर state को निरीक्षित करना और यह देखना कि किस action ने रन किया आसान होता है बजाय कि बेतरतीब reactive ऑब्जेक्ट्स की खोज करने के।
डंपिंग-ग्राउंड स्टोर से बचें
एक ग्लोबल स्टोर पहले व्यवस्थित लगता है, पर जल्दी ही यह एक जंक ड्रॉयर बन जाता है। Pinia के लिए अच्छे उम्मीदवार वे सच में साझा चिंताएँ हैं जैसे user identity और permissions, चयनित workspace, feature flags, और कई स्क्रीन में उपयोग होने वाला साझा reference data।
पेज-ओनली चिंताओं (जैसे किसी फॉर्म का अस्थायी इनपुट) को लोकल रखें जब तक कि कई रूट्स को वाकई उनकी ज़रूरत न हो।
उदाहरण 1: स्टोर नहीं बनाते हुए फ़िल्टर और तालिकाएँ
एक Orders पेज की कल्पना करें: एक तालिका, फ़िल्टर (status, date range, customer), pagination, और एक साइड पैनल जो चयनित order का प्रीव्यू दिखाता है। यह जल्दी गड़बड़ हो सकता है क्योंकि हर फ़िल्टर और तालिका सेटिंग को एक ग्लोबल स्टोर में डालने का प्रलोभन होता है।
चुनने का सरल तरीका यह तय करना है कि क्या याद रखना चाहिए और कहाँ:
- मैमोरी केवल (लोकल या provide/inject): पेज छोड़ने पर रिसेट हो। डिस्पोजेबल state के लिए अच्छा।
- क्वेरी पैरामीटर: शेयर करने योग्य और रीलोड पर बचता है। फ़िल्टर और pagination के लिए अच्छा।
- Pinia: नेविगेशन के बाद भी बचता है। “वापस उसी तरह सूची पाना” के लिए अच्छा।
इसके बाद इम्प्लिमेंटेशन आमतौर पर ऐसा होता है:
अगर कोई उम्मीद नहीं करता कि सेटिंग्स नेविगेशन के बाद बचेंगी, तो filters, sort, page, और pageSize को Orders पेज कंपोनेंट के अंदर रखें और वह पेज fetch ट्रिगर करे। अगर toolbar, table, और preview पैनल को वही मॉडल चाहिए और prop drilling noisy हो रहा है, तो सूची मॉडल को पेज शेल पर ले जाएँ और provide/inject से साझा करें। अगर आप सूची को रूट्स के पार sticky महसूस कराना चाहते हैं (ऑर्डर खोलें, अन्य पेज पर जाएँ, वापस लौटें और वही फ़िल्टर और चयन देखें), तो Pinia बेहतर फिट है।
एक व्यावहारिक नियम: लोकल से शुरू करें, जब कई child कंपोनेंट्स को वही मॉडल चाहिए तो provide/inject पर जाएँ, और केवल तभी Pinia का इस्तेमाल करें जब क्रॉस-रूट पर संग्रह आवश्यक हो।
उदाहरण 2: ड्राफ्ट्स और अनसेव्ड एडिट्स (ऐसे फॉर्म जिन पर लोग भरोसा करें)
एक सपोर्ट एजेंट ग्राहक रिकॉर्ड संपादित कर रहा है: संपर्क विवरण, बिलिंग जानकारी, और आंतरिक नोट्स। वे बाधित होते हैं, स्क्रीन बदलते हैं, फिर वापस आते हैं। अगर फॉर्म उनका काम भूल जाए या आधा-लिखा डेटा सेव कर दे, तो भरोसा टूट जाता है।
ड्राफ्ट्स के लिए तीन चीज़ें अलग रखें: आख़िरी सेव्ड रिकॉर्ड, उपयोगकर्ता के staged edits, और UI-केवल state जैसे validation errors।
लोकल state: स्पष्ट dirty नियम के साथ staged edits
अगर एडिट स्क्रीन आत्म-निहित है, तो लोकल कंपोनेंट state अक्सर सबसे सुरक्षित होता है। रिकॉर्ड लोड करें, उसे क्लोन करके draft बनाएं, draft को एडिट करें, और केवल Save क्लिक करने पर ही सर्वर पर भेजें। Cancel draft को छोड़ देता है और रिकॉर्ड फिर से लोड होता है।
provide/inject: नेस्टेड सेक्शन्स में एक ड्राफ्ट साझा करना
एडमिन फॉर्म अक्सर टैब या पैनलों में विभाजित होते हैं (Profile, Addresses, Permissions)। provide/inject के साथ आप एक ड्राफ्ट मॉडल रख सकते हैं और एक छोटा API एक्सपोज़ कर सकते हैं जैसे updateField(), resetDraft(), और validateSection()। हर सेक्शन उसी ड्राफ्ट को पढ़ता और लिखता है बिना पाँच लेयर्स से props पास किए।
कब Pinia ड्राफ्ट्स में मददगार होता है
जब ड्राफ्ट्स नेविगेशन के पार बचने चाहिए या एडिट स्क्रीन के बाहर दिखाई देने चाहिए तब Pinia उपयोगी होता है। एक सामान्य पैटर्न है draftsById[customerId], ताकि हर रिकॉर्ड का अपना ड्राफ्ट हो। इससे जब उपयोगकर्ता कई एडिट स्क्रीन खोल सकते हैं तो भी मदद मिलती है।
ड्राफ्ट बग आमतौर पर कुछ ही सामान्य गलतियों से आते हैं: रिकॉर्ड लोड होने से पहले ड्राफ्ट बनाना, रिफेच पर एक dirty ड्राफ्ट को ओवरराइट कर देना, cancel पर errors साफ़ न करना, या एक ही shared key का उपयोग करना जिससे ड्राफ्ट ओवरराइट हो जाए। यदि आप स्पष्ट नियम सेट करते हैं (कभी बनाना, ओवरराइट करना, छोड़ना, पर्सिस्ट करना, और save के बाद प्रतिस्थापित करना), तो ज्यादातर समस्याएँ गायब हो जाती हैं।
यदि आप AppMaster (appmaster.io) के साथ एडमिन स्क्रीन बनाते हैं, तो "ड्राफ्ट बनाम सेव्ड रिकॉर्ड" का विभाजन वही लागू होता है: ड्राफ्ट क्लाइंट पर रखें, और बैकएंड को केवल सफल Save के बाद सत्य माना जाए।
उदाहरण 3: मल्टी-टैब एडिटिंग बिना स्टेट टकराव के
मल्टी-टैब एडिटिंग वही जगह है जहाँ एडमिन पैनल अक्सर टूटते हैं। उपयोगकर्ता Customer A खोलता है, फिर Customer B, फिर वापस स्विच करता है, और उम्मीद करता है कि हर टैब अपने अनसेव्ड बदलाव याद रखे।
समाधान यह है कि हर टैब को उसका अपना state बंडल मॉडल करें, न कि एक साझा ड्राफ्ट के रूप में। हर टैब को कम से कम एक unique key चाहिए (अक्सर रिकॉर्ड ID पर आधारित), ड्राफ्ट डेटा, स्थिति (clean, dirty, saving), और फ़ील्ड errors।
यदि टैब एक स्क्रीन के अंदर रहते हैं तो लोकल दृष्टिकोण अच्छी तरह काम करता है। पेज कंपोनेंट टैब सूची और ड्राफ्ट्स का मालिक रखे। हर एडिटर पैनल केवल अपने बंडल को पढ़े और लिखे। जब टैब बंद हो, उस बंडल को हटा दें—समस्या हल। यह अलगाव चीज़ों को आसान और समझने योग्य रखता है।
किसी भी जगह स्टेट रहे, उसकी आकृति समान होती है:
- टैब ऑब्जेक्ट्स की सूची (प्रत्येक में अपना
customerId,draft,status, औरerrors) - एक
activeTabKey - एक्शन्स जैसे
openTab(id),updateDraft(key, patch),saveTab(key), औरcloseTab(key)
जब टैब्स नेविगेशन के पार बचने चाहिए (Orders खोलें और वापस आएँ) या कई स्क्रीन टैब खोल/फोकस करें, तब Pinia बेहतर विकल्प बनता है। ऐसे में एक छोटा "tab manager" स्टोर व्यवहार को पूरे ऐप में संगत रखता है।
मुख्य टकराव से बचना: एक ग्लीबली वैरिएबल जैसे currentDraft मत रखें। यह तब तक काम करता है जब तक दूसरी टैब न खुले—फिर एडिट्स ओवरराइट होंगी, validation errors गलत जगह दिखेंगे, और Save गलत रिकॉर्ड अपडेट कर देगा। जब हर ओपन टैब का अपना बंडल हो, तो collisions डिजाइन के हिसाब से ज़्यादातर गायब हो जाती हैं।
ऐसी सामान्य गलतियाँ जो बग और गंदा कोड बनाती हैं
ज्यादातर एडमिन पैनल बग "Vue बग" नहीं होते—वे state बग होते हैं: डेटा गलत जगह रहता है, स्क्रीन के दो हिस्से असहमति करते हैं, या पुराना state चुपचाप बचा रहता है।
सबसे अक्सर दिखने वाले पैटर्न:
- डिफ़ॉल्ट रूप से सब कुछ Pinia में डालना ownership को अस्पष्ट बनाता है। एक ग्लोबल स्टोर पहले व्यवस्थित लगेगा, पर जल्द ही हर पेज वही ऑब्जेक्ट्स पढ़ने और लिखने लगता है, और क्लीनअप अटक जाता है।
provide/injectबिना स्पष्ट कॉन्ट्रैक्ट के उपयोग करने से छुपी निर्भरताएँ बन जाती हैं। अगर एक childfiltersinject करता है पर यह स्पष्ट नहीं कि कौन provide करता है और किस एक्शन को बदलने की अनुमति है, तो किसी दूसरे child के म्यूटेशन से अचानक अपडेट आते हैं।- एक ही स्टोर में server state और UI state मिलाना आकस्मिक ओवरराइट्स का कारण बनता है। फेच्ड रिकॉर्ड्स अलग व्यवहार करते हैं बनाम "ड्रॉअर खुला है?", "कौन सा टैब" या "dirty fields"। जब वे साथ रहते हैं, refetching UI को रौंद सकता है या UI बदलाव cached data को म्यूटेट कर सकता है।
- लाइफसाइकिल क्लीनअप छोड़ने से state लीक हो जाती है। एक व्यू के फ़िल्टर दूसरे पर असर कर सकते हैं, और ड्राफ्ट्स पेज छोड़ने के बाद भी रह सकती हैं। अगली बार जब कोई अलग रिकॉर्ड खोलेगा तो उन्हें पुरानी चयनाएँ दिखेंगी और वे ऐप को खराब समझेंगे।
- ड्राफ्ट्स की खराब कींग एक चुपचाप भरोसा तोड़ देती है। यदि आप
draft:editUserजैसी एक ही key के नीचे ड्राफ्ट्स स्टोर करते हैं, तो User A और User B का संपादन एक-दूसरे को ओवरराइट कर देगा।
एक सरल नियम इनमें से अधिकांश से बचाता है: state को जितना संभव हो उतना नज़दीक रखें जहाँ वह उपयोग हो रही है, और केवल तभी उठाएँ जब दो स्वतंत्र हिस्सों को सचमुच साझा करने की ज़रूरत हो। जब आप शेयर करते हैं, ownership (कौन बदल सकता है) और identity (कैसे की किया गया है) दोनों स्पष्ट करें।
एक त्वरित चेकलिस्ट इससे पहले कि आप लोकल, provide/inject, या Pinia चुनें
सबसे उपयोगी प्रश्न है: इस state का मालिक कौन है? अगर आप इसे एक वाक्य में नहीं कह सकते तो संभावना है कि state बहुत ज़्यादा काम कर रहा है और विभाजित होना चाहिए।
इन चेक्स को तुरंत फ़िल्टर की तरह इस्तेमाल करें:
- क्या आप मालिक नामित कर सकते हैं (एक कंपोनेंट, एक पेज, या पूरा ऐप)?
- क्या इसे रूट बदलाव या रीलोड से बचना चाहिए? अगर हाँ, तो persistence की योजना बनाएं बजाय इसके कि ब्राउज़र से उम्मीद करें।
- क्या दो रिकॉर्ड्स कभी एक साथ एडिट हो सकते हैं? अगर हाँ, तो state को रिकॉर्ड ID से की-कीजिए।
- क्या यह state केवल एक पेज शेल के नीचे उपयोग हो रही है? अगर हाँ, तो अक्सर
provide/injectफिट बैठता है। - क्या आपको बदलावों को निरीक्षित करने और यह समझने की ज़रूरत है कि किसने क्या बदला? अगर हाँ, तो Pinia अक्सर उस स्लाइस के लिए साफ़ जगह है।
टूल मैचिंग, साधारण शब्दों में:
यदि state एक कंपोनेंट के अंदर पैदा और खत्म होती है (जैसे एक dropdown open/closed flag), तो लोकल रखें। यदि एक ही स्क्रीन पर कई कंपोनेंट्स को साझा संदर्भ चाहिए (filter bar + table + summary), तो provide/inject साझा रखता है बिना उसे ग्लोबल बनाने के। यदि state स्क्रीन-के-पार साझा करनी है, नेविगेशन के बाद बचनी है, या अनुमान्य, डिबग करने योग्य अपडेट चाहिए, तो Pinia का इस्तेमाल करें और ड्राफ्ट्स में रिकॉर्ड ID द्वारा एंट्रीज़ की-कीजिए।
यदि आप Vue 3 एडमिन UI बना रहे हैं (AppMaster द्वारा जनरेट की गई किसी भी चीज़ सहित), यह चेकलिस्ट ज़रूरत से पहले सब कुछ स्टोर में डालने से बचाती है।
अगला कदम: state को बिना गड़बड़ी के बढ़ाना
एडमिन पैनलों में state मैनेजमेंट सुधारने का सबसे सुरक्षित तरीका छोटे, नीरस कदमों में बढ़ना है। किसी भी पेज-ओनली चीज़ के लिए पहले लोकल रखें। जब आप वास्तविक reuse देखें (कॉपी किया गया लॉजिक, तीसरा कंपोनेंट जिसे वही state चाहिए), तो इसे एक स्तर ऊपर ले जाएँ। तभी साझा स्टोर पर विचार करें।
एक मार्ग जो अधिकांश टीमों के लिए काम करता है:
- पेज-ओनली state पहले लोकल रखें (फ़िल्टर, sort, pagination, खुले/बंद पैनल)।
- जब एक ही पेज पर कई कंपोनेंट्स को साझा संदर्भ चाहिए तो
provide/injectउपयोग करें। - क्रॉस-स्क्रीन जरूरतों के लिए एक-एक करके Pinia स्टोर जोड़ें (draft manager, tab manager, current workspace)।
- रिसेट नियम लिखें और उस पर कायम रहें (नेविगेशन, लॉगआउट, Clear filters, Discard changes पर क्या रिसेट होता है)।
रिसेट नियम छोटे लगते हैं, पर वे अधिकांश "यह क्यों बदला?" पलों को रोकते हैं। उदाहरण के लिए तय करें कि जब कोई व्यक्ति कोई अलग रिकॉर्ड खोले और वापस आए तो ड्राफ्ट के साथ क्या होता है: restore, warn, या reset। फिर उस व्यवहार को सुसंगत बनाएँ।
यदि आप स्टोर पेश करते हैं, तो उसे feature-shaped रखें। एक drafts स्टोर को केवल ड्राफ्ट बनाना, पुनर्स्थापित करना, और साफ़ करना चाहिए—यह तालिका फ़िल्टर या UI layout flags का मालिक नहीं होना चाहिए।
अगर आप तेज़ी से कोई एडमिन पैनल प्रोटोटाइप करना चाहते हैं, तो AppMaster (appmaster.io) Vue3 वेब ऐप और बैकएंड जनरेट कर सकता है, और आप-generated कोड को तब भी परिष्कृत कर सकते हैं जहाँ आपको कस्टम state हैंडलिंग चाहिए। एक व्यावहारिक अगला कदम है एक स्क्रीन एंड-टू-एंड बनाना (उदा. एक एडिट फॉर्म ड्राफ्ट रिकवरी के साथ) और देखना कि वास्तव में क्या Pinia की ज़रूरत है और क्या लोकल ही काफी है।
सामान्य प्रश्न
लोकल state का उपयोग तब करें जब डेटा केवल एक कंपोनेंट को प्रभावित करता हो और उस कंपोनेंट के अनमाउंट होते ही रिसेट हो सकता हो। साधारण उदाहरण: एक डायलॉग का open/close, एक तालिका में चयनित रो, या कोई फॉर्म सेक्शन जो कहीं और reuse नहीं होता।
provide/inject का उपयोग तब करें जब एक ही पेज पर कई कंपोनेंट्स को एक साझा source of truth चाहिए और prop drilling परेशानी बना रहा हो। जो आप provide करते हैं उसे छोटा और इरादतन रखें ताकि पेज पढ़ने में आसान रहे।
जब state को routes के बीच साझा करना हो, navigation के बाद बचाए रखना हो, या एक जगह से निरीक्षण और डिबग करना आसान चाहिए—तब Pinia सबसे स्पष्ट संकेत है। आम उदाहरण: current workspace, permissions, feature flags, और क्रॉस-स्क्रीन मैनेजर्स जैसे drafts या tabs।
पहले state का प्रकार नाम दें (UI, server, form, cross-screen), फिर स्कोप तय करें (एक कंपोनेंट, एक पेज, कई routes), लाइफटाइम (अनमाउंट पर रिसेट, नेविगेशन पर रहे, रीलोड पर रहे), और concurrency (एक एडिटर या मल्टी-टैब)। इन चार लेबल से आमतौर पर टूल जेनरेट हो जाता है।
यदि उपयोगकर्ता view शेयर या रिस्टोर करना चाहते हैं, तो फ़िल्टर और पेजिनेशन क्वेरी पैरामीटर में रखें ताकि वे रीलोड पर बचे और कॉपी हो सकें। यदि लोग बस “वापस उसी लिस्ट पर मिलना चाहते हैं” तो Pinia में स्टोर करना ठीक है; अन्यथा पेज-स्कोप रखें।
सहेजे गए रिकॉर्ड और उपयोगकर्ता के ड्राफ्ट को अलग रखें, और केवल Save पर बैकएंड को लिखें। एक स्पष्ट dirty नियम रखें और नेविगेशन पर क्या होता है (warn, auto-save, या recoverable draft) तय करें ताकि उपयोगकर्ता का काम न खोए।
हर ओपन एडिटर को उसका अपना स्टेट बंडल दें जो रिकॉर्ड ID से की-कीया गया हो (कभी-कभी एक टैब की भी आवश्यकता होती है)। एक ग्लोबल currentDraft से बचें क्योंकि दूसरी टैब खुलते ही ओवरराइट होने का खतरा रहता है।
यदि पूरा एडिट फ्लो एक ही रूट में सीमित है तो पेज-ओंड provide/inject काम कर सकता है। यदि ड्राफ्ट्स को रूट के पार बनाए रखना है या एडिट स्क्रीन के बाहर दिखाना है तो Pinia में draftsById[recordId] जैसा स्ट्रक्चर बेहतर और अनुमान योग्य होता है।
जो चीज़ आप compute कर सकते हैं उसे स्टोर न करें। बैजेस, सारांश और “can save” जैसे फ्लैग्स को computed से निकालें ताकि वे सिंक से नहीं भटकेँ।
सामान्य गलतियाँ: सब कुछ Pinia में डाल देना, सर्वर रिस्पॉन्स और UI टॉगल्ज़ को एक साथ मिलाना, और नेविगेशन पर क्लीनअप न करना। साथ ही एक ही ड्राफ्ट की खराब कींग भी भरोसा तोड़ देती है—एक साझा key पर कई रिकॉर्ड्स का ड्राफ्ट न रखें।


