एक शुद्ध reducer के साथ जटिल state को व्यवस्थित करें, जो ट्रांज़िशन को केंद्रीकृत करता है और असंभव संयोजनों को खत्म कर देता है।
इस पाठ को Kodokon में खोलेंजैसे ही कोई कंपोनेंट कई ऐसे state मानों को संभालता है जो साथ-साथ बदलते हैं - एक request की स्थिति, कुछ डेटा, एक error - useState कॉल बढ़ाने से ट्रांज़िशन का तर्क हर event handler में बिखर जाता है। आप लक्षण जानते हैं: सही क्रम में तीन setter कॉल करने होते हैं, और देर-सवेर एक असंगत state बन जाती है, जैसे success की स्थिति के साथ एक non-null error बैठी हो। useReducer ज़िम्मेदारी को उलट देता है: state एक अकेली object बन जाती है, और हर बदलाव एक action से होकर गुज़रता है जिसे एक केंद्रीय फ़ंक्शन, यानी reducer, व्याख्यायित करता है।
function requestReducer(state, action) {
switch (action.type) {
case "start":
return { status: "loading", data: null, error: null };
case "resolve":
return {
status: "success",
data: action.data,
error: null,
};
case "reject":
return {
status: "error",
data: null,
error: action.error,
};
default:
throw new Error("Unknown action: " + action.type);
}
}reducer एक शुद्ध फ़ंक्शन है: यह वर्तमान state और एक action प्राप्त करता है, फिर बिना किसी side effect के अगली state लौटाता है। फ़ायदे तुरंत मिलते हैं: ट्रांज़िशन स्पष्ट और संपूर्ण होते हैं, असंभव states मूल स्तर पर ही खत्म हो जाती हैं (हर case एक संगत object फिर से बनाता है), और तर्क बिखरने के बजाय एक ही जगह पढ़ा जाता है। एक उपयोगी चेतावनी संकेत: यदि आप एक ही setState कॉल के क्रम को दो जगहों पर copy-paste करते हुए पाएं, तो शायद आपके पास एक reducer की कमी है।
function UserProfile() {
const [state, dispatch] = useReducer(requestReducer, {
status: "idle",
data: null,
error: null,
});
async function load(id) {
dispatch({ type: "start" });
try {
const res = await fetch("/api/users/" + id);
const data = await res.json();
dispatch({ type: "resolve", data });
} catch (error) {
dispatch({ type: "reject", error });
}
}
return <button onClick={() => load(1)}>Load</button>;
}useState और useReducer के बीच चुनाव पसंद का मामला नहीं है। सरल, स्वतंत्र मानों के लिए useState रखें; useReducer की ओर तब बढ़ें जब अगली state पिछली पर निर्भर हो या जब कई फ़ील्ड साथ-साथ बदलें। दो अतिरिक्त फ़ायदे: तीसरा argument आलसी initialization देता है (useReducer(reducer, arg, init) केवल पहले render पर init चलाता है), और React गारंटी देता है कि dispatch एक render से दूसरे render तक वही identity बनाए रखे - आप इसे बिना useCallback के children को पास कर सकते हैं।