Eesti

Süvenege Reacti useReducer hook'i, et hallata tõhusalt keerukaid rakenduse olekuid, parandades globaalsete Reacti projektide jõudlust ja hooldatavust.

Reacti useReducer muster: keeruka olekuhalduse meisterlik valdamine

Pidevalt areneval esirakenduste arendusmaastikul on React end kehtestanud juhtiva raamistikuna kasutajaliideste ehitamiseks. Rakenduste keerukuse kasvades muutub oleku haldamine üha keerulisemaks. useState hook pakub lihtsat viisi oleku haldamiseks komponendi sees, kuid keerukamate stsenaariumide jaoks pakub React võimsat alternatiivi: useReducer hook'i. See blogipostitus süveneb useReducer mustrisse, uurides selle eeliseid, praktilisi rakendusi ja seda, kuidas see saab oluliselt täiustada teie Reacti rakendusi globaalselt.

Vajaduse mõistmine keeruka olekuhalduse järele

Reacti rakenduste ehitamisel puutume sageli kokku olukordadega, kus komponendi olek ei ole pelgalt lihtne väärtus, vaid pigem omavahel seotud andmepunktide kogum või olek, mis sõltub eelmistest olekuväärtustest. Mõelge järgmistele näidetele:

Nendes stsenaariumides võib ainuüksi useState'i kasutamine viia keeruka ja raskesti hallatava koodini. Mitme olekumuutuja värskendamine ühe sündmuse vastusena võib muutuda tülikaks ning nende värskenduste haldamise loogika võib hajuda üle komponendi, muutes selle mõistmise ja hooldamise keeruliseks. Siin tulebki appi useReducer.

useReducer hook'i tutvustus

useReducer hook on alternatiiv useState'ile keeruka olekuloogika haldamiseks. See põhineb Reduxi mustri põhimõtetel, kuid on implementeeritud Reacti komponendi enda sees, kaotades paljudel juhtudel vajaduse eraldi välise teegi järele. See võimaldab teil tsentraliseerida oma olekuvärskenduste loogika ühte funktsiooni, mida nimetatakse redutseerijaks (reducer).

useReducer hook võtab kaks argumenti:

Hook tagastab massiivi, mis sisaldab kahte elementi:

Redutseerija funktsioon

Redutseerija funktsioon on useReducer mustri süda. See on puhas funktsioon, mis tähendab, et sellel ei tohiks olla kõrvalmõjusid (nagu API-päringute tegemine või globaalsete muutujate muutmine) ja see peaks alati tagastama sama sisendi korral sama väljundi. Redutseerija funktsioon võtab kaks argumenti:

Redutseerija funktsiooni sees kasutate switch-lauset või if/else if-lauseid, et käsitleda erinevaid tegevustüüpe ja vastavalt sellele olekut värskendada. See tsentraliseerib teie olekuvärskenduste loogika ja muudab lihtsamaks arutleda, kuidas olek erinevate sündmuste vastusena muutub.

Dispatch-funktsioon

Dispatch-funktsioon on meetod, mida kasutate olekuvärskenduste käivitamiseks. Kui kutsute välja dispatch(action), edastatakse tegevus redutseerija funktsioonile, mis seejärel värskendab olekut tegevuse tüübi ja payload'i alusel.

Praktiline näide: loenduri implementeerimine

Alustame lihtsa näitega: loenduri komponent. See illustreerib põhikontseptsioone enne keerukamate näidete juurde liikumist. Loome loenduri, mis saab suurendada, vähendada ja lähtestada:


import React, { useReducer } from 'react';

// Tegevustüüpide defineerimine
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';

// Redutseerija funktsiooni defineerimine
function counterReducer(state, action) {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + 1 };
    case DECREMENT:
      return { count: state.count - 1 };
    case RESET:
      return { count: 0 };
    default:
      return state;
  }
}

function Counter() {
  // useReducer'i lähtestamine
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>Arv: {state.count}</p>
      <button onClick={() => dispatch({ type: INCREMENT })}>Suurenda</button>
      <button onClick={() => dispatch({ type: DECREMENT })}>Vähenda</button>
      <button onClick={() => dispatch({ type: RESET })}>Lähtesta</button>
    </div>
  );
}

export default Counter;

Selles näites:

Loenduri näite laiendamine: payload'i lisamine

Muudame loendurit, et see võimaldaks suurendamist kindla väärtuse võrra. See tutvustab payload'i kontseptsiooni tegevuses:


import React, { useReducer } from 'react';

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';
const SET_VALUE = 'SET_VALUE';

function counterReducer(state, action) {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + action.payload };
    case DECREMENT:
      return { count: state.count - action.payload };
    case RESET:
      return { count: 0 };
    case SET_VALUE:
      return { count: action.payload };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });
  const [inputValue, setInputValue] = React.useState(1);

  return (
    <div>
      <p>Arv: {state.count}</p>
      <button onClick={() => dispatch({ type: INCREMENT, payload: parseInt(inputValue) || 1 })}>Suurenda {inputValue} võrra</button>
      <button onClick={() => dispatch({ type: DECREMENT, payload: parseInt(inputValue) || 1 })}>Vähenda {inputValue} võrra</button>
      <button onClick={() => dispatch({ type: RESET })}>Lähtesta</button>
       <input
         type="number"
         value={inputValue}
         onChange={(e) => setInputValue(e.target.value)}
       />
      </div>
  );
}

export default Counter;

Selles laiendatud näites:

useReducer'i kasutamise eelised

useReducer muster pakub mitmeid eeliseid võrreldes useState'i otsese kasutamisega keeruka olekuhalduse puhul:

Millal kasutada useReducer'it

Kuigi useReducer pakub olulisi eeliseid, ei ole see alati õige valik. Kaaluge useReducer'i kasutamist, kui:

Lihtsate olekuvärskenduste jaoks on useState sageli piisav ja lihtsam kasutada. Otsuse tegemisel arvestage oma oleku keerukust ja potentsiaalset kasvu.

Täpsemad kontseptsioonid ja tehnikad

useReducer'i kombineerimine Contextiga

Globaalse oleku haldamiseks või oleku jagamiseks mitme komponendi vahel saate kombineerida useReducer'it Reacti Context API-ga. See lähenemine on sageli eelistatud Reduxile väiksemate ja keskmise suurusega projektide puhul, kus te ei soovi lisada täiendavaid sõltuvusi.


import React, { createContext, useReducer, useContext } from 'react';

// Tegevustüüpide ja redutseerija defineerimine (nagu varem)
const INCREMENT = 'INCREMENT';
// ... (teised tegevustüübid ja counterReducer funktsioon)

const CounterContext = createContext();

function CounterProvider({ children }) {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
}

function useCounter() {
  return useContext(CounterContext);
}

function Counter() {
  const { state, dispatch } = useCounter();

  return (
    <div>
      <p>Arv: {state.count}</p>
      <button onClick={() => dispatch({ type: INCREMENT })}>Suurenda</button>
    </div>
  );
}

function App() {
  return (
    <CounterProvider>
      <Counter />
    </CounterProvider>
  );
}

export default App;

Selles näites:

useReducer'i testimine

Redutseerijate testimine on lihtne, sest need on puhtad funktsioonid. Saate redutseerija funktsiooni hõlpsasti eraldi testida, kasutades ühiktestimise raamistikku nagu Jest või Mocha. Siin on näide Jesti abil:


import { counterReducer } from './counterReducer'; // Eeldades, et counterReducer on eraldi failis

const INCREMENT = 'INCREMENT';

describe('counterReducer', () => {
  it('peaks suurendama arvu', () => {
    const state = { count: 0 };
    const action = { type: INCREMENT };
    const newState = counterReducer(state, action);
    expect(newState.count).toBe(1);
  });

   it('peaks tagastama sama oleku tundmatute tegevustüüpide korral', () => {
        const state = { count: 10 };
        const action = { type: 'UNKNOWN_ACTION' };
        const newState = counterReducer(state, action);
        expect(newState).toBe(state); // Kinnita, et olek ei ole muutunud
    });
});

Redutseerijate testimine tagab, et need käituvad ootuspäraselt ja muudab teie olekuloogika refaktoriseerimise lihtsamaks. See on kriitiline samm robustsete ja hooldatavate rakenduste ehitamisel.

Jõudluse optimeerimine memoiseerimisega

Keerukate olekute ja sagedaste värskendustega töötamisel kaaluge useMemo kasutamist oma komponentide jõudluse optimeerimiseks, eriti kui teil on olekust tuletatud väärtusi. Näiteks:


import React, { useReducer, useMemo } from 'react';

function reducer(state, action) {
  // ... (redutseerija loogika) 
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, initialState);

  // Arvuta tuletatud väärtus, memoiseerides selle useMemo abil
  const derivedValue = useMemo(() => {
    // Kallis arvutus, mis põhineb olekul
    return state.value1 + state.value2;
  }, [state.value1, state.value2]); // Sõltuvused: arvuta uuesti ainult siis, kui need väärtused muutuvad

  return (
    <div>
      <p>Tuletatud väärtus: {derivedValue}</p>
      <button onClick={() => dispatch({ type: 'UPDATE_VALUE1', payload: 10 })}>Uuenda väärtust 1</button>
      <button onClick={() => dispatch({ type: 'UPDATE_VALUE2', payload: 20 })}>Uuenda väärtust 2</button>
    </div>
  );
}

Selles näites arvutatakse derivedValue ainult siis, kui state.value1 või state.value2 muutuvad, vältides tarbetuid arvutusi igal uuesti renderdamisel. See lähenemine on tavaline praktika optimaalse renderdusjõudluse tagamiseks.

Reaalse maailma näited ja kasutusjuhud

Uurime mõningaid praktilisi näiteid, kus useReducer on väärtuslik tööriist Reacti rakenduste ehitamisel globaalsele publikule. Pange tähele, et need näited on lihtsustatud põhikontseptsioonide illustreerimiseks. Tegelikud implementatsioonid võivad sisaldada keerukamat loogikat ja sõltuvusi.

1. E-kaubanduse toodete filtrid

Kujutage ette e-kaubanduse veebisaiti (mõelge populaarsetele platvormidele nagu Amazon või AliExpress, mis on saadaval globaalselt) suure tootekataloogiga. Kasutajad peavad saama tooteid filtreerida erinevate kriteeriumide alusel (hinnavahemik, bränd, suurus, värv, päritoluriik jne). useReducer on ideaalne filtri oleku haldamiseks.


import React, { useReducer } from 'react';

const initialState = {
  priceRange: { min: 0, max: 1000 },
  brand: [], // Valitud brändide massiiv
  color: [], // Valitud värvide massiiv
  //... muud filtri kriteeriumid
};

function filterReducer(state, action) {
  switch (action.type) {
    case 'UPDATE_PRICE_RANGE':
      return { ...state, priceRange: action.payload };
    case 'TOGGLE_BRAND':
      const brand = action.payload;
      return { ...state, brand: state.brand.includes(brand) ? state.brand.filter(b => b !== brand) : [...state.brand, brand] };
    case 'TOGGLE_COLOR':
      // Sarnane loogika värvi filtreerimiseks
      return { ...state, color: state.color.includes(action.payload) ? state.color.filter(c => c !== action.payload) : [...state.color, action.payload] };
    // ... muud filtri tegevused
    default:
      return state;
  }
}

function ProductFilter() {
  const [state, dispatch] = useReducer(filterReducer, initialState);

  // UI komponendid filtri kriteeriumide valimiseks ja dispatch-tegevuste käivitamiseks
  // Näiteks: vahemiku sisend hinna jaoks, märkeruudud brändide jaoks jne.

  return (
    <div>
      <!-- Filtri UI elemendid -->
    </div>
  );
}

See näide näitab, kuidas hallata mitut filtri kriteeriumit kontrollitud viisil. Kui kasutaja muudab mis tahes filtri seadet (hind, bränd jne), värskendab redutseerija vastavalt filtri olekut. Toodete kuvamise eest vastutav komponent kasutab seejärel uuendatud olekut kuvatavate toodete filtreerimiseks. See muster toetab keerukate filtreerimissüsteemide ehitamist, mis on levinud globaalsetel e-kaubanduse platvormidel.

2. Mitmeastmelised vormid (nt rahvusvahelised saatmisvormid)

Paljud rakendused hõlmavad mitmeastmelisi vorme, näiteks need, mida kasutatakse rahvusvaheliseks saatmiseks või keerukate nõuetega kasutajakontode loomiseks. useReducer sobib suurepäraselt selliste vormide oleku haldamiseks.


import React, { useReducer } from 'react';

const initialState = {
  step: 1, // Praegune samm vormis
  formData: {
    firstName: '',
    lastName: '',
    address: '',
    city: '',
    country: '',
    // ... muud vormiväljad
  },
  errors: {},
};

function formReducer(state, action) {
  switch (action.type) {
    case 'NEXT_STEP':
      return { ...state, step: state.step + 1 };
    case 'PREV_STEP':
      return { ...state, step: state.step - 1 };
    case 'UPDATE_FIELD':
      return { ...state, formData: { ...state.formData, [action.payload.field]: action.payload.value } };
    case 'SET_ERRORS':
      return { ...state, errors: action.payload };
    case 'SUBMIT_FORM':
      // Käsitle siin vormi esitamise loogikat, nt API-päringud
      return state;
    default:
      return state;
  }
}

function MultiStepForm() {
  const [state, dispatch] = useReducer(formReducer, initialState);

  // Renderdusloogika iga vormi sammu jaoks
  // Põhineb praegusel sammul olekus
  const renderStep = () => {
    switch (state.step) {
      case 1:
        return <Step1 formData={state.formData} dispatch={dispatch} />;
      case 2:
        return <Step2 formData={state.formData} dispatch={dispatch} />;
      // ... muud sammud
      default:
        return <p>Kehtetu samm</p>;
    }
  };

  return (
    <div>
      {renderStep()}
      <!-- Navigatsiooninupud (Järgmine, Eelmine, Esita) vastavalt praegusele sammule -->
    </div>
  );
}

See illustreerib, kuidas hallata erinevaid vormivälju, samme ja võimalikke valideerimisvigu struktureeritud ja hooldataval viisil. See on kriitilise tähtsusega kasutajasõbralike registreerimis- või kassaprotsesside ehitamisel, eriti rahvusvahelistele kasutajatele, kellel võivad olla erinevad ootused, mis põhinevad nende kohalikel tavadel ja kogemustel erinevate platvormidega nagu Facebook või WeChat.

3. Reaalajas rakendused (vestlus, koostöövahendid)

useReducer on kasulik reaalajas rakenduste jaoks, nagu koostöövahendid nagu Google Docs või sõnumirakendused. See käsitleb sündmusi nagu sõnumite saamine, kasutaja liitumine/lahkumine ja ühenduse staatus, tagades, et kasutajaliides värskendub vastavalt vajadusele.


import React, { useReducer, useEffect } from 'react';

const initialState = {
  messages: [],
  users: [],
  connectionStatus: 'connecting',
};

function chatReducer(state, action) {
  switch (action.type) {
    case 'RECEIVE_MESSAGE':
      return { ...state, messages: [...state.messages, action.payload] };
    case 'USER_JOINED':
      return { ...state, users: [...state.users, action.payload] };
    case 'USER_LEFT':
      return { ...state, users: state.users.filter(user => user.id !== action.payload.id) };
    case 'SET_CONNECTION_STATUS':
      return { ...state, connectionStatus: action.payload };
    default:
      return state;
  }
}

function ChatRoom() {
  const [state, dispatch] = useReducer(chatReducer, initialState);

  useEffect(() => {
    // Loo WebSocket ühendus (näide):
    const socket = new WebSocket('wss://your-websocket-server.com');

    socket.onopen = () => dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'connected' });
    socket.onmessage = (event) => dispatch({ type: 'RECEIVE_MESSAGE', payload: JSON.parse(event.data) });
    socket.onclose = () => dispatch({ type: 'SET_CONNECTION_STATUS', payload: 'disconnected' });

    return () => socket.close(); // Puhastamine lahtivõtmisel
  }, []);

  // Renderda sõnumid, kasutajate nimekiri ja ühenduse staatus vastavalt olekule
  return (
    <div>
      <p>Ühenduse staatus: {state.connectionStatus}</p>
      <!-- UI sõnumite kuvamiseks, kasutajate nimekiri ja sõnumite saatmine -->
    </div>
  );
}

See näide annab aluse reaalajas vestluse haldamiseks. Olek käsitleb sõnumite salvestamist, vestluses olevaid kasutajaid ja ühenduse staatust. useEffect hook vastutab WebSocket-ühenduse loomise ja sissetulevate sõnumite käsitlemise eest. See lähenemine loob reageeriva ja dünaamilise kasutajaliidese, mis teenindab kasutajaid üle maailma.

Parimad tavad useReducer'i kasutamiseks

Et useReducer'it tõhusalt kasutada ja hooldatavaid rakendusi luua, kaaluge neid parimaid tavasid:

Kokkuvõte

useReducer hook on võimas ja mitmekülgne tööriist keeruka oleku haldamiseks Reacti rakendustes. See pakub arvukalt eeliseid, sealhulgas tsentraliseeritud olekuloogikat, paremat koodi organiseeritust ja täiustatud testitavust. Järgides parimaid tavasid ja mõistes selle põhikontseptsioone, saate useReducer'i abil ehitada robustsemaid, hooldatavamaid ja jõudlusvõimelisemaid Reacti rakendusi. See muster annab teile võimaluse tõhusalt lahendada keerukaid olekuhalduse väljakutseid, võimaldades teil ehitada globaalseks kasutuseks valmis rakendusi, mis pakuvad sujuvaid kasutajakogemusi kogu maailmas.

Süvenedes Reacti arendusse, viib useReducer mustri lisamine teie tööriistakasti kahtlemata puhtamate, skaleeritavamate ja kergemini hooldatavate koodibaasideni. Pidage meeles, et alati tuleb arvestada oma rakenduse spetsiifiliste vajadustega ja valida igas olukorras parim lähenemine olekuhaldusele. Head kodeerimist!