Naršykite React'o useReducer kablį sudėtingos būsenos valdymui. Šis vadovas apima pažangius šablonus, našumo optimizavimą ir realaus pasaulio pavyzdžius kūrėjams visame pasaulyje.
React useReducer: Sudėtingų būsenos valdymo šablonų įvaldymas
React'o useReducer kabliukas yra galingas įrankis sudėtingos būsenos valdymui jūsų programose. Skirtingai nei useState, kuris dažnai tinka paprastesniems būsenos atnaujinimams, useReducer puikiai tinka dirbant su sudėtinga būsenos logika ir atnaujinimais, kurie priklauso nuo ankstesnės būsenos. Šis išsamus vadovas gilinsis į useReducer subtilybes, nagrinės pažangius šablonus ir pateiks praktinių pavyzdžių kūrėjams visame pasaulyje.
useReducer pagrindų supratimas
Iš esmės, useReducer yra būsenos valdymo įrankis, įkvėptas Redux šablono. Jis priima du argumentus: reduktoriaus funkciją ir pradinę būseną. Reduktoriaus funkcija apdoroja būsenos perėjimus, pagrįstus išsiųstomis veiksmomis. Šis šablonas skatina švaresnį kodą, lengvesnį derinimą ir nuspėjamus būsenos atnaujinimus, o tai yra labai svarbu bet kokio dydžio programoms. Išnagrinėkime komponentus:
- Reduktoriaus funkcija: Tai yra
useReducerširdis. Ji priima dabartinę būseną ir veiksmo objektą kaip įvestį ir grąžina naują būseną. Veiksmo objektas paprastai turitypesavybę, kuri aprašo atliekamą veiksmą ir gali turėtipayloadsu papildomais duomenimis. - Pradinė būsena: Tai jūsų programos būsenos pradžios taškas.
- Išsiuntimo funkcija (Dispatch Function): Ši funkcija leidžia inicijuoti būsenos atnaujinimus išsiunčiant veiksmus. Išsiuntimo funkciją (dispatch) teikia
useReducer.
Štai paprastas pavyzdys, iliustruojantis pagrindinę struktūrą:
import React, { useReducer } from 'react';
// Define the reducer function
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
// Initialize useReducer
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;
Šiame pavyzdyje reduktoriaus funkcija apdoroja inkrementavimo ir dekrementavimo veiksmus, atnaujindama `count` būseną. dispatch funkcija naudojama šiems būsenos perėjimams inicijuoti.
Pažangūs useReducer šablonai
Nors pagrindinis useReducer šablonas yra paprastas, tik pradėjus dirbti su sudėtingesne būsenos logika, jo tikroji galia pasidaro akivaizdi. Štai keletas pažangių šablonų, kuriuos verta apsvarstyti:
1. Sudėtingi veiksmo duomenų paketai (Payloads)
Veiksmai nebūtinai turi būti paprastos eilutės, tokios kaip „increment“ arba „decrement“. Jie gali perduoti turtingą informaciją. Naudodami duomenų paketus galite perduoti duomenis reduktoriui dinamiškesniems būsenos atnaujinimams. Tai ypač naudinga formoms, API iškvietimams ir sąrašų valdymui.
function reducer(state, action) {
switch (action.type) {
case 'add_item':
return { ...state, items: [...state.items, action.payload] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
default:
return state;
}
}
// Example action dispatch
dispatch({ type: 'add_item', payload: { id: 1, name: 'Item 1' } });
dispatch({ type: 'remove_item', payload: 1 }); // Remove item with id 1
2. Kelių reduktorių naudojimas (reduktorių kompozicija)
Didelėse programose visų būsenos perėjimų valdymas viename reduktoriuje gali tapti nepatogus. Reduktorių kompozicija leidžia suskaidyti būsenos valdymą į mažesnes, lengviau valdomas dalis. Tai galite pasiekti sujungdami kelis reduktorius į vieną aukščiausio lygio reduktorių.
// Individual Reducers
function itemReducer(state, action) {
switch (action.type) {
case 'add_item':
return { ...state, items: [...state.items, action.payload] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
default:
return state;
}
}
function filterReducer(state, action) {
switch(action.type) {
case 'SET_FILTER':
return {...state, filter: action.payload}
default:
return state;
}
}
// Combining Reducers
function combinedReducer(state, action) {
return {
items: itemReducer(state.items, action),
filter: filterReducer(state.filter, action)
};
}
// Initial state (Example)
const initialState = {
items: [],
filter: 'all'
};
function App() {
const [state, dispatch] = useReducer(combinedReducer, initialState);
return (
<div>
{/* UI Components that trigger actions on combinedReducer */}
</div>
);
}
3. `useReducer` naudojimas su Context API
„Context API“ suteikia galimybę perduoti duomenis per komponentų medį, nereikalaujant rankiniu būdu perduoti savybių kiekviename lygyje. Kartu su useReducer, jis sukuria galingą ir efektyvų būsenos valdymo sprendimą, dažnai laikomą lengva alternatyva Redux. Šis šablonas yra ypač naudingas globaliai programos būsenai valdyti.
import React, { createContext, useContext, useReducer } from 'react';
// Create a context for our state
const AppContext = createContext();
// Define the reducer and initial state (as before)
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
const initialState = { count: 0 };
// Create a provider component
function AppProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
}
// Create a custom hook for easy access
function useAppState() {
return useContext(AppContext);
}
function Counter() {
const { state, dispatch } = useAppState();
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
function App() {
return (
<AppProvider>
<Counter />
</AppProvider>
);
}
Čia AppContext teikia būseną ir išsiuntimo (dispatch) funkciją visiems vaikiniams komponentams. Pasirinktinis kabliukas useAppState supaprastina prieigą prie konteksto.
4. „Thunks“ įdiegimas (asinchroniniai veiksmai)
useReducer pagal numatytuosius nustatymus yra sinchroninis. Tačiau daugelyje programų reikės atlikti asinchronines operacijas, pavyzdžiui, duomenų gavimą iš API. „Thunks“ leidžia atlikti asinchroninius veiksmus. Tai galite pasiekti išsiunčiant funkciją (vadinamą „thunk“) vietoj paprasto veiksmo objekto. Funkcija gaus `dispatch` funkciją ir tada galės išsiųsti kelis veiksmus, atsižvelgiant į asinchroninės operacijos rezultatą.
function fetchUserData(userId) {
return async (dispatch) => {
dispatch({ type: 'request_user' });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
dispatch({ type: 'receive_user', payload: user });
} catch (error) {
dispatch({ type: 'request_user_error', payload: error });
}
};
}
function reducer(state, action) {
switch (action.type) {
case 'request_user':
return { ...state, loading: true, error: null };
case 'receive_user':
return { ...state, loading: false, user: action.payload, error: null };
case 'request_user_error':
return { ...state, loading: false, error: action.payload };
default:
return state;
}
}
function UserProfile({ userId }) {
const [state, dispatch] = useReducer(reducer, { loading: false, user: null, error: null });
React.useEffect(() => {
dispatch(fetchUserData(userId));
}, [userId, dispatch]);
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error.message}</p>;
if (!state.user) return null;
return (
<div>
<h2>{state.user.name}</h2>
<p>Email: {state.user.email}</p>
</div>
);
}
Šis pavyzdys išsiunčia veiksmus įkėlimui, sėkmei ir klaidų būsenoms asinchroninio API iškvietimo metu. Sudėtingesniems scenarijams jums gali prireikti tarpinės programinės įrangos, tokios kaip `redux-thunk`; tačiau paprastesniais atvejais šis šablonas veikia labai gerai.
Našumo optimizavimo technikos
„React“ programų našumo optimizavimas yra labai svarbus, ypač dirbant su sudėtingu būsenos valdymu. Štai keletas metodų, kuriuos galite taikyti naudodami useReducer:
1. Išsiuntimo (Dispatch) funkcijos memoizavimas
useReducer teikiama dispatch funkcija paprastai nesikeičia tarp atvaizdavimų, tačiau vis tiek rekomenduojama ją memoizuoti, jei perduodate ją vaikiniams komponentams, kad išvengtumėte nereikalingo pakartotinio atvaizdavimo. Tam naudokite React.useCallback:
const [state, dispatch] = useReducer(reducer, initialState);
const memoizedDispatch = React.useCallback(dispatch, []); // Memoize dispatch function
Tai užtikrina, kad dispatch funkcija keičiasi tik tada, kai pasikeičia priklausomybės priklausomybės masyve (šiuo atveju jų nėra, todėl ji nesikeis).
2. Reduktoriaus logikos optimizavimas
Reduktoriaus funkcija vykdoma kiekvieno būsenos atnaujinimo metu. Užtikrinkite, kad jūsų reduktorius būtų našus, sumažindami nereikalingus skaičiavimus ir vengdami sudėtingų operacijų reduktoriaus funkcijoje. Apsvarstykite šiuos dalykus:
- Nekintami būsenos atnaujinimai: Visada atnaujinkite būseną nekeisdami jos. Naudokite išskleidimo operatorių (
...) arbaObject.assign(), kad sukurtumėte naujus būsenos objektus, užuot tiesiogiai keitę esamus. Tai svarbu pokyčių aptikimui ir netikėto elgesio išvengimui. - Venkite nereikalingų gilių kopijų: Giliai kopijuokite būsenos objektus tik tada, kai tai absoliučiai būtina. Paviršinės kopijos (naudojant išskleidimo operatorių paprastiems objektams) paprastai yra pakankamos ir yra mažiau skaičiavimų reikalaujančios.
- Tingus inicializavimas: Jei pradinės būsenos skaičiavimas yra brangus kompiuteriniu požiūriu, galite naudoti funkciją būsenai inicializuoti. Ši funkcija bus vykdoma tik vieną kartą, pradinio atvaizdavimo metu.
//Lazy initialization
const [state, dispatch] = useReducer(reducer, initialState, (initialArg) => {
//Expensive initialization logic here
return {
...initialArg,
initializedData: 'data'
}
});
3. Sudėtingų skaičiavimų memoizavimas naudojant `useMemo`
Jei jūsų komponentai atlieka skaičiavimų brangias operacijas, pagrįstas būsena, naudokite React.useMemo rezultatui memoizuoti. Tai leidžia išvengti skaičiavimo pakartotinio paleidimo, nebent pasikeičia priklausomybės. Tai yra labai svarbu didelių programų ar programų su sudėtinga logika našumui.
import React, { useReducer, useMemo } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { items: [1, 2, 3, 4, 5] });
const total = useMemo(() => {
console.log('Calculating total...'); // This will only log when the dependencies change
return state.items.reduce((sum, item) => sum + item, 0);
}, [state.items]); // Dependency array: recalculate when items change
return (
<div>
<p>Total: {total}</p>
{/* ... other components ... */}
</div>
);
}
Realaus pasaulio useReducer pavyzdžiai
Pažvelkime į keletą praktinių useReducer naudojimo atvejų, kurie iliustruoja jo universalumą. Šie pavyzdžiai yra svarbūs kūrėjams visame pasaulyje, įvairių tipų projektuose.
1. Formos būsenos valdymas
Formos yra dažnas bet kurios programos komponentas. useReducer yra puikus būdas valdyti sudėtingą formos būseną, įskaitant kelis įvesties laukus, validavimą ir pateikimo logiką. Šis šablonas skatina palaikomumą ir mažina šabloninio kodo kiekį.
import React, { useReducer } from 'react';
function formReducer(state, action) {
switch (action.type) {
case 'change':
return {
...state,
[action.field]: action.value,
};
case 'submit':
//Perform submission logic (API calls, etc.)
return state;
case 'reset':
return {name: '', email: '', message: ''};
default:
return state;
}
}
function ContactForm() {
const [state, dispatch] = useReducer(formReducer, { name: '', email: '', message: '' });
const handleSubmit = (event) => {
event.preventDefault();
dispatch({type: 'submit'});
// Example API Call (Conceptual)
// fetch('/api/contact', { method: 'POST', body: JSON.stringify(state) });
alert('Form submitted (conceptually)!')
dispatch({type: 'reset'});
};
const handleChange = (event) => {
dispatch({ type: 'change', field: event.target.name, value: event.target.value });
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name:</label>
<input type="text" id="name" name="name" value={state.name} onChange={handleChange} />
<label htmlFor="email">Email:</label>
<input type="email" id="email" name="email" value={state.email} onChange={handleChange} />
<label htmlFor="message">Message:</label>
<textarea id="message" name="message" value={state.message} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
export default ContactForm;
Šis pavyzdys efektyviai valdo formos laukų būseną ir apdoroja tiek įvesties pokyčius, tiek formos pateikimą. Atkreipkite dėmesį į `reset` veiksmą, skirtą formos atstatymui po sėkmingo pateikimo. Tai glaustas ir lengvai suprantamas įdiegimas.
2. Pirkinių krepšelio įdiegimas
Elektroninės komercijos programos, kurios yra populiarios visame pasaulyje, dažnai apima pirkinių krepšelio valdymą. useReducer puikiai tinka tvarkyti prekių pridėjimo, pašalinimo ir atnaujinimo krepšelyje sudėtingumą.
function cartReducer(state, action) {
switch (action.type) {
case 'add_item':
const existingItemIndex = state.items.findIndex(item => item.id === action.payload.id);
if (existingItemIndex !== -1) {
// If item exists, increment the quantity
const updatedItems = [...state.items];
updatedItems[existingItemIndex] = { ...updatedItems[existingItemIndex], quantity: updatedItems[existingItemIndex].quantity + 1 };
return { ...state, items: updatedItems };
}
return { ...state, items: [...state.items, { ...action.payload, quantity: 1 }] };
case 'remove_item':
return { ...state, items: state.items.filter(item => item.id !== action.payload) };
case 'update_quantity':
const itemIndex = state.items.findIndex(item => item.id === action.payload.id);
if (itemIndex !== -1) {
const updatedItems = [...state.items];
updatedItems[itemIndex] = { ...updatedItems[itemIndex], quantity: action.payload.quantity };
return { ...state, items: updatedItems };
}
return state;
case 'clear_cart':
return { ...state, items: [] };
default:
return state;
}
}
function ShoppingCart() {
const [state, dispatch] = React.useReducer(cartReducer, { items: [] });
const handleAddItem = (item) => {
dispatch({ type: 'add_item', payload: item });
};
const handleRemoveItem = (itemId) => {
dispatch({ type: 'remove_item', payload: itemId });
};
const handleUpdateQuantity = (itemId, quantity) => {
dispatch({ type: 'update_quantity', payload: {id: itemId, quantity} });
}
// Calculate total
const total = React.useMemo(() => {
return state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}, [state.items]);
return (
<div>
<h2>Shopping Cart</h2>
{state.items.length === 0 && <p>Your cart is empty.</p>}
<ul>
{state.items.map(item => (
<li key={item.id}>
{item.name} - ${item.price} x {item.quantity} = ${item.price * item.quantity}
<button onClick={() => handleRemoveItem(item.id)}>Remove</button>
<input type="number" min="1" value={item.quantity} onChange={(e) => handleUpdateQuantity(item.id, parseInt(e.target.value))} />
</li>
))}
</ul>
<p>Total: ${total}</p>
<button onClick={() => dispatch({ type: 'clear_cart' })}>Clear Cart</button>
{/* ... other components ... */}
</div>
);
}
Krepšelio reduktorius valdo prekių pridėjimą, pašalinimą ir atnaujinimą, įskaitant jų kiekius. React.useMemo kabliukas naudojamas efektyviam bendros kainos skaičiavimui. Tai yra dažnas ir praktiškas pavyzdys, nepaisant vartotojo geografinės padėties.
3. Paprasto perjungiklio įdiegimas su išliekančia būsena
Šis pavyzdys demonstruoja, kaip sujungti useReducer su vietine saugykla (local storage) ilgalaikei būsenai. Vartotojai dažnai tikisi, kad jų nustatymai bus prisiminti. Šis šablonas naudoja naršyklės vietinę saugyklą perjungiklio būsenai išsaugoti, net ir atnaujinus puslapį. Tai puikiai tinka temoms, vartotojo nuostatoms ir kt.
import React, { useReducer, useEffect } from 'react';
// Reducer function
function toggleReducer(state, action) {
switch (action.type) {
case 'toggle':
return { isOn: !state.isOn };
default:
return state;
}
}
function ToggleWithPersistence() {
// Retrieve the initial state from local storage or default to false
const [state, dispatch] = useReducer(toggleReducer, { isOn: JSON.parse(localStorage.getItem('toggleState')) || false });
// Use useEffect to save the state to local storage whenever it changes
useEffect(() => {
localStorage.setItem('toggleState', JSON.stringify(state.isOn));
}, [state.isOn]);
return (
<div>
<button onClick={() => dispatch({ type: 'toggle' })}>
{state.isOn ? 'On' : 'Off'}
</button>
<p>Toggle is: {state.isOn ? 'On' : 'Off'}</p>
</div>
);
}
export default ToggleWithPersistence;
Šis paprastas komponentas perjungia būseną ir išsaugo ją į `localStorage`. useEffect kabliukas užtikrina, kad būsena būtų išsaugota kiekvieno atnaujinimo metu. Šis šablonas yra galingas įrankis vartotojo nustatymams išsaugoti tarp sesijų, o tai svarbu globaliu mastu.
Kada rinktis useReducer vietoj useState
Apsisprendimas tarp useReducer ir useState priklauso nuo jūsų būsenos sudėtingumo ir to, kaip ji keičiasi. Štai vadovas, padėsiantis jums teisingai pasirinkti:
- Rinkitės
useReducer, kai: - Jūsų būsenos logika yra sudėtinga ir apima kelias subreikšmes.
- Kita būsena priklauso nuo ankstesnės būsenos.
- Jums reikia valdyti būsenos atnaujinimus, apimančius daugybę veiksmų.
- Norite centralizuoti būsenos logiką ir palengvinti derinimą.
- Numatote, kad vėliau reikės išplėsti savo programą arba pertvarkyti būsenos valdymą.
- Rinkitės
useState, kai: - Jūsų būsena yra paprasta ir atspindi vieną reikšmę.
- Būsenos atnaujinimai yra paprasti ir nepriklauso nuo ankstesnės būsenos.
- Turite palyginti nedidelį skaičių būsenos atnaujinimų.
- Norite greito ir paprasto sprendimo pagrindiniam būsenos valdymui.
Paprastai, jei rašote sudėtingą logiką savo useState atnaujinimo funkcijose, tai yra geras ženklas, kad useReducer gali būti tinkamesnis. useReducer kabliukas dažnai leidžia sukurti švaresnį ir lengviau prižiūrimą kodą sudėtingų būsenos perėjimų situacijose. Jis taip pat gali padėti lengviau atlikti kodo vienetinį testavimą, nes suteikia nuoseklų mechanizmą būsenos atnaujinimams atlikti.
Geriausia praktika ir svarstymai
Norėdami maksimaliai išnaudoti useReducer, atsižvelkite į šias geriausias praktikas ir svarstymus:
- Tvarkykite veiksmus: Apibrėžkite savo veiksmų tipus kaip konstantas (pvz., `const INCREMENT = 'increment';`), kad išvengtumėte klaidų ir padarytumėte savo kodą lengviau prižiūrimą. Apsvarstykite galimybę naudoti veiksmų kūrimo šabloną, kad inkapsuliuotumėte veiksmų kūrimą.
- Tipų tikrinimas: Didesniems projektams apsvarstykite galimybę naudoti „TypeScript“, kad nustatytumėte savo būsenos, veiksmų ir reduktoriaus funkcijos tipus. Tai padės išvengti klaidų ir pagerins kodo skaitomumą bei palaikomumą.
- Testavimas: Rašykite vienetinius testus savo reduktoriaus funkcijoms, kad užtikrintumėte, jog jos elgiasi teisingai ir apdoroja skirtingus veiksmų scenarijus. Tai labai svarbu siekiant užtikrinti, kad jūsų būsenos atnaujinimai būtų nuspėjami ir patikimi.
- Našumo stebėjimas: Naudokite naršyklės kūrėjo įrankius (pvz., „React DevTools“) arba našumo stebėjimo įrankius, kad stebėtumėte savo komponentų našumą ir nustatytumėte bet kokius kliūtis, susijusias su būsenos atnaujinimais.
- Būsenos formos dizainas: Kruopščiai sukurkite savo būsenos formą, kad išvengtumėte nereikalingo įdėjimo ar sudėtingumo. Gerai struktūrizuota būsena palengvins jos supratimą ir valdymą.
- Dokumentacija: Aiškiai dokumentuokite savo reduktoriaus funkcijas ir veiksmų tipus, ypač bendradarbiavimo projektuose. Tai padės kitiems kūrėjams suprasti jūsų kodą ir palengvins jo priežiūrą.
- Apsvarstykite alternatyvas (Redux, Zustand ir kt.): Labai didelėms programoms, turinčioms ypač sudėtingus būsenos reikalavimus, arba jei jūsų komanda jau yra susipažinusi su „Redux“, galite apsvarstyti galimybę naudoti išsamesnę būsenos valdymo biblioteką. Tačiau
useReducerir „Context API“ siūlo galingą sprendimą be papildomo išorinių bibliotekų sudėtingumo.
Išvada
React'o useReducer kabliukas yra galingas ir lankstus įrankis sudėtingos būsenos valdymui jūsų programose. Supratę jo pagrindus, įvaldę pažangius šablonus ir įgyvendinę našumo optimizavimo technikas, galite kurti tvirtesnius, lengviau prižiūrimus ir efektyvesnius „React“ komponentus. Nepamirškite pritaikyti savo požiūrio pagal savo projekto poreikius. Nuo sudėtingų formų valdymo iki pirkinių krepšelių kūrimo ir nuolatinių nuostatų tvarkymo, useReducer įgalina kūrėjus visame pasaulyje kurti sudėtingas ir patogias vartotojui sąsajas. Gilinantis į „React“ kūrimo pasaulį, useReducer įvaldymas pasirodys esantis neįkainojamas jūsų įrankių rinkinyje. Visada prisiminkite, kad pirmenybę teikiate kodo aiškumui ir palaikomumui, kad jūsų programos liktų lengvai suprantamos ir tobulėtų laikui bėgant.