Refaktorering af useState() For at brugeReducer()

Denne artikel blev oprindeligt publiceret på www.aboutmonica.com.

Jeg har for nylig oprettet en Stimulus Check Calculator baseret på tal fra CARES Act og Washington Post for at hjælpe folk med at vurdere mængden af ​​deres stimuluscheck i henhold til CARES Act.
Denne artikel vil gennemgå, hvordan jeg refaktorerede regnemaskinens tilstandsstyring ved at konsolidere flere useState() React hooks i en enkelt useReducer() . useReducer() er et alternativ, der kan overvejes, når du bruger useState() at styre tilstand i funktionelle React-komponenter. Denne artikel forudsætter en vis fortrolighed med statsstyring i React and React Hooks.


Skærmbillede af Stimulus Check Calculator.

Oversigt:

  • Administrer formulartilstand med useState();
    • Indstilling af starttilstanden med useState()
    • Opdaterer formulartilstand med useState()
  • Administrer formulartilstand med useReducer();
    • Indstilling af starttilstanden med useReducer()
    • Opdaterer formulartilstand med useReducer()
  • Fuld kildekode med eksempler
    • kildekode ved hjælp af useState():
    • kildekode ved hjælp af useReducer():

Håndtering af formulartilstand med useState();

For at bruge useState() Reaktionskrog til tilstandsstyring af den lommeregner, som jeg først skulle importere useState fra React.

import { useState } from "react";

Indstilling af starttilstanden med useState()

Derefter inden for den funktion, der returnerer Form komponent I opsætter useState() kroge til taxYear , filingStatus ,income , children og stimulusAmount .

  const { SINGLE, HEADOFHOUSE, MARRIED } = filingStatuses;
  const [taxYear, setTaxYear] = useState(2019);
  const [filingStatus, setFilingStatus] = useState(SINGLE);
  const [income, setIncome] = useState("75000");
  const [children, setChildren] = useState(0);
  const [stimulusAmount, setStimulusAmount] = useState(-1);

Parameteren blev overført til useState() repræsenterer standardværdien for den pågældende tilstand. Det betyder, at linjen nedenfor angiver standardværdien taxYear i tilstand til 2019 .

const [taxYear, setTaxYear] = useState(2019);

Opdaterer formulartilstand med useState()

Hændelseshandlere, såsom onChange eller onClick kan bruges til at opdatere komponentens tilstand, når data, når en formular ændres. Administration af formulartilstand ved at opdatere komponentens interne tilstand betragtes som en "kontrolleret komponent" i forhold til at lade DOM administrere formularens tilstand.

For at opdatere taxYear 's værdi til det valgte år, er der en onClick hændelseshandler, der kalder setTaxYear(year) med year parameter er den aktuelle year der er valgt.

 {[2019, 2018].map(year => (
            <button
              onClick={() => setTaxYear(year)}
              className={year == taxYear ? "selectedButton" : ""}
              key={year}
              name="tax-year"
            >
              {year == 2019 ? "Yes" : "No"}
            </button>
          ))}

Lignende logik bruges til at opdatere filingStatus income og children , stimulusAmount og handleSubmit når formulardata opdateres eller indsendes.

## Håndtering af formulartilstand med useReducer();

For at bruge useReducer() React hook til tilstandsstyring af den lommeregner, jeg først skulle bruge for at importere useReducer fra React. Hvis du ikke er bekendt med reducers i JavaScript tjek min artikel om Understanding Reduce i Javascript

import { useReducer } from "react";

Indstilling af starttilstanden med useReducer()

Så indstiller jeg starttilstanden for komponenten som:

const initialState = {
    taxYear: 2019,
    filingStatus: SINGLE,
    income: "75000",
    children: 0,
    stimulusAmount: -1,
  };

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

Svarende til useState , useReducer returnerer den relaterede tilstand sammen med en metode til at opdatere tilstanden. Med useReducer i stedet for at opdatere tilstanden ved at sende en værdi til setState() en handling skal afsendes, som kalder reducer .

I mit tilfælde reducer funktion så sådan ud:

  function reducer(state, action) {
  const { type, payload } = action;
  return { ...state, [type]: payload };
}

Opdaterer formulartilstand med useReducer()

Hver gang dispatch hedder det skal kaldes med en action element, der indeholder en type og i dette særlige tilfælde en payload såvel. Skatteårets tilstand kan opdateres onClick ved at skyde

 onClick={() => dispatch({ type: "taxYear", payload: year })}

i stedet for

    onClick={() => setTaxYear(year)}

reducer(state, action) forventer at modtage en action det er et objekt med type og payload . Inden for reduceringsfunktionen er handlingens type og payload bruges til at returnere den aktuelle state med [type]: payload overskrevet.

  const { type, payload } = action;
  return { ...state, [type]: payload };

I tilfælde af opdatering af staten fra 2017, hvis den nuværende tilstand var:

const initialState = {
    taxYear: 2019,
    filingStatus: SINGLE,
    income: "75000",
    children: 0,
    stimulusAmount: -1,
  };

Affyr derefter onClick={() => dispatch({ type: "taxYear", payload: 2018 })} ville resultere i, at reducereren returnerer den aktuelle tilstand, men kun med værdien taxYear overskrevet og indstillet til 2018. Bemærk:dette fungerer som skrevet, fordi for hver handling i dette eksempel type handling er den samme som dens tilsvarende nøgleværdi i state .

Fuld kildekode med eksempler

Den fulde kildekode nedenfor sammenligner de fulde implementeringer af tilstandsstyringsmetoderne ovenfor. Som illustreret ovenfor, useReducer() er en anden React hook, der kan bruges til tilstandsstyring og kan implementeres på en måde, der tillader logik fra useState() kroge, der skal konsolideres. Den relaterede kildekode til den aktuelle version af lommeregneren er tilgængelig på GitHub.

kildekode ved hjælp af useState():


import { filingStatuses } from "../utils/constants";
import { getStimulusAmount } from "../utils/calculateStimulus";
import { useState } from "react";



function Form() {
  const { SINGLE, HEADOFHOUSE, MARRIED } = filingStatuses;
  const [taxYear, setTaxYear] = useState(2019);
  const [filingStatus, setFilingStatus] = useState(SINGLE);
  const [income, setIncome] = useState("75000");
  const [children, setChildren] = useState(0);
  const [stimulusAmount, setStimulusAmount] = useState(-1);

  function handleSubmit(e) {
    e.preventDefault();
    setStimulusAmount(calculateStimulus(income, filingStatus, children));
  }

  return (
        <form onSubmit={handleSubmit}>
          <label htmlFor="tax-year">Have you filed your 2019 taxes yet?</label>
          {[2019, 2018].map(year => (
            <button
              onClick={() => setTaxYear(year)}
              className={year == taxYear ? "selectedButton" : ""}
              key={year}
              name="tax-year"
            >
              {year == 2019 ? "Yes" : "No"}
            </button>
          ))}
          <label htmlFor="filing-status">
            What was your filing status in your {taxYear} taxes?{" "}
          </label>
          {[SINGLE, MARRIED, HEADOFHOUSE].map(status => (
            <button
              onClick={() => setFilingStatus(status)}
              className={status == filingStatus ? "selectedButton" : ""}
              name="filing-status"
              key={status}
            >
              {" "}
              {status}
            </button>
          ))}
          <br />
          <label htmlFor="adjusted-income">
            What was your adjusted gross income in {taxYear}?
          </label>
          ${" "}
          <input
            type="number"
            inputMode="numeric"
            pattern="[0-9]*"
            value={income}
            onChange={e => setIncome(e.target.value)}
            min={0}
            name="adjusted-income"
          />
          <br />
          <label htmlFor="children">
            How many children under age 17 did you claim as dependents in{" "}
            {taxYear}?
          </label>
          <input
            type="number"
            inputMode="numeric"
            pattern="[0-9]*"
            value={children}
            onChange={e => setChildren(e.target.value)}
            min={0}
            name="label"
          />
          <br />
          <button type="submit" className="calculateButton">
            Calculate
          </button>
          <p>
            {" "}
            {stimulusAmount >= 0 &&
              (stimulusAmount > 0
                ? `Your stimulus amount is expected to be $${stimulusAmount}.`
                : `You are not expected to receive a stimulus.`)}
          </p>
          <br />
        </form>
  );
}

export default Form;

kildekode ved hjælp af useReducer():

import { useReducer } from "react";
import { filingStatuses } from "../utils/constants";
import { getStimulusAmount } from "../utils/calculateStimulus";

function reducer(state, action) {
  const { type, payload } = action;
  return { ...state, [type]: payload };
}

function Form() {
  const { SINGLE, HEADOFHOUSE, MARRIED } = filingStatuses;

  const initialState = {
    taxYear: 2019,
    filingStatus: SINGLE,
    income: "75000",
    children: 0,
    stimulusAmount: -1,
  };

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

  function handleSubmit(e) {
    e.preventDefault();
    dispatch({
      type: "stimulusAmount",
      payload: getStimulusAmount(income, filingStatus, children),
    });
  }

  const { taxYear, filingStatus, income, children, stimulusAmount } = state;

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="tax-year">Have you filed your 2019 taxes yet?</label>
      {[2019, 2018].map((year) => (
        <button
          onClick={() => dispatch({ type: "taxYear", payload: year })}
          className={year == taxYear ? "selectedButton" : ""}
          key={year}
          name="tax-year"
        >
          {year == 2019 ? "Yes" : "No"}
        </button>
      ))}
      <label htmlFor="filing-status">
        What was your filing status in your {taxYear} taxes?{" "}
      </label>
      {[SINGLE, MARRIED, HEADOFHOUSE].map((status) => (
        <button
          onClick={() => dispatch({ type: "filingStatus", payload: status })}
          className={status == filingStatus ? "selectedButton" : ""}
          name="filing-status"
          key={status}
        >
          {" "}
          {status}
        </button>
      ))}
      <br />
      <label htmlFor="adjusted-income">
        What was your adjusted gross income in {taxYear}?
      </label>
      ${" "}
      <input
        type="string"
        inputMode="numeric"
        pattern="[0-9]*"
        value={income}
        onChange={(e) => dispatch({ type: "income", payload: e.target.value })}
        min={0}
      />
      <br />
      <label htmlFor="children">
        How many children under age 17 did you claim as dependents in {taxYear}?
      </label>
      <input
        type="number"
        inputMode="numeric"
        pattern="[0-9]*"
        value={children}
        onChange={(e) =>
          dispatch({ type: "children", payload: e.target.value })
        }
        min={0}
        name="label"
      />
      <br />
      <button type="submit" className="calculateButton">
        Calculate
      </button>
      <p>
        {" "}
        {stimulusAmount >= 0 &&
          (stimulusAmount > 0
            ? `Your stimulus amount is likely to be ${new Intl.NumberFormat(
                "en-US",
                { style: "currency", currency: "USD" }
              ).format(stimulusAmount)}.`
            : `You are not expected to receive a stimulus.`)}
      </p>
      <br />
    </form>
  );
}

export default Form;