Sådan ændres et eksisterende objekt i et JavaScript-array

Et mønster til ændring af et bestemt objekt i et array ved hjælp af JavaScript.

Kom godt i gang

Til denne tutorial vil vi bruge CheatCode Next.js Boilerplate for at give os et udgangspunkt for vores arbejde. Lad os først klone en kopi:

Terminal

git clone https://github.com/cheatcode/nextjs-boilerplate.git

Installer derefter afhængighederne for kedelpladen:

Terminal

cd nextjs-boilerplate && npm install

Til sidst skal du starte udviklingsserveren:

Terminal

npm run dev

Dermed er vi klar til at komme i gang.

Opbygning af en React-komponent til test

For at kontekstualisere vores arbejde vil vi bygge en enkel, klassebaseret React-komponent. Dette vil give os en situation, hvor det giver mere mening at bruge det mønster, vi lærer.

/pages/index.js

import React from "react";
import PropTypes from "prop-types";
import usersFixture from "../lib/users";

class Index extends React.Component {
  state = {};

  render() {
    return <div></div>;
  }
}

Index.propTypes = {};

export default Index;

Her er vi netop ved at skabe stilladset til en klassekomponent i React. Den del, vi vil være opmærksomme på, er navnet af komponenten Index og stien til denne fil /pages/index.js . Fordi vi bruger Next.js, er vi her afhængige af Next.js-routeren ved at placere vores komponentfil inde i frameworkets /pages bibliotek.

Filer og mapper her konverteres automatisk til ruter. Fordi vi har placeret dette i roden af vores /pages mappe som index.js , vil dette gengives ved roden URL til vores applikation, eller http://localhost:5000/ .

Lad os derefter tage et hurtigt kig på den usersFixture fil, vi har importeret øverst:

/lib/users.js

const users = [
  {
    _id: "f91bbFE72aaDDd8c",
    emailAddress: "[email protected]",
    name: { first: "Phoebe", last: "Schamberger" },
    address: {
      streetAddress: "39473 David Mill",
      city: "Stammbury",
      state: "Michigan",
      zipCode: "91802",
    },
  },
  {
    _id: "E8c8f6d3fE6761dd",
    emailAddress: "[email protected]",
    name: { first: "Orin", last: "Balistreri" },
    address: {
      streetAddress: "27846 Collier Roads",
      city: "Schneiderton",
      state: "Kansas",
      zipCode: "49705-7399",
    },
  },
  {
    _id: "Cd9caEcb4fB1D558",
    emailAddress: "[email protected]",
    name: { first: "Chanelle", last: "Oberbrunner" },
    address: {
      streetAddress: "638 Fadel Cliffs",
      city: "Lake Thorahaven",
      state: "West Virginia",
      zipCode: "12349-0480",
    },
  },
  {
    _id: "BAf1DcEec4b4DBAc",
    emailAddress: "[email protected]",
    name: { first: "Briana", last: "White" },
    address: {
      streetAddress: "0540 Brown Meadow",
      city: "Port Jerad",
      state: "Oklahoma",
      zipCode: "14368",
    },
  },
  {
    _id: "1c4E8Aa24c37cBFA",
    emailAddress: "[email protected]",
    name: { first: "Vidal", last: "Stokes" },
    address: {
      streetAddress: "31028 Marquardt Forest",
      city: "North Bethany",
      state: "Indiana",
      zipCode: "32632",
    },
  },
];

export default users;

Her har vi en statisk liste over "brugere" (disse består af falske data). Vores mål er at indlæse denne række af brugere i vores komponent og derefter foretage ændringer af objekterne i arrayet via JavaScript.

/pages/index.js

import React from "react";
import PropTypes from "prop-types";
import usersFixture from "../lib/users";

class Index extends React.Component {
  state = {
    users: usersFixture,
  };

  render() {
    const { users } = this.state;

    return (
      <div>
        <header className="page-header">
          <h4>Test</h4>
        </header>
        <div className="responsive-table">
          <table className="table align-middle">
            <thead>
              <tr>
                <th>Name</th>
                <th>Email Address</th>
                <th>Address</th>
                <th />
              </tr>
            </thead>
            <tbody>
              {users.map(({ _id, name, emailAddress, address }) => {
                return (
                  <tr key={_id}>
                    <td>
                      {name?.first} {name?.last}
                    </td>
                    <td>{emailAddress}</td>
                    <td>
                      {address?.streetAddress} {address?.city}, {address?.state}{" "}
                      {address?.zipCode}
                    </td>
                    <td>
                      <button
                        disabled={editingUser}
                        className="btn btn-primary"
                        onClick={() => {
                          this.setState({ editingUser: _id });
                        }}
                      >
                        Edit
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    );
  }
}

Index.propTypes = {};

export default Index;

Tilbage i vores komponent har vi nu taget usersFixture vi importerede tidligere og indstiller på vores komponents state værdi som users . Nede i render() funktion, har vi returneret noget HTML for at gengive vores brugerliste i en tabel. Her er de CSS-klassenavne, du ser, taget fra Bootstrap CSS-rammeværket. Brugen af ​​disse klasser her påvirker ikke vores faktiske arbejde – de bruges kun til præsentation.

Den del, vi holder af, er, når vi .map() over users værdi, vi placerede på tilstand (igen, dette er vores statiske række af brugerobjekter). Inde i vores render() metode, bruger vi JavaScript-destrukturering til at "plukke af" users fra this.state og derefter i den returnerede HTML-markup (teknisk JSX, som er et React-specifikt sprog, der ligner HTML) gengiver vi en <table></table> med vores users bliver listet ud i kroppen.

For "noteringen" holder vi tingene enkle. Her gengiver vi en <tr></tr> tabelrække for hver bruger, der viser deres name , emailAddress , og fysisk address . Igen er disse værdier blot testdata for at hjælpe os med at kontekstualisere vores arbejde med at ændre objekter i en matrix.

Til sidst, for hver bruger, har vi tilføjet en <button></button> som, når der klikkes på den, indstiller denne bruger som editingUser på staten. Her sender vi brugerens _id (deres unikke ID i vores "database") for at sige "vi redigerer i øjeblikket brugeren med denne _id .

Lad os derefter tilslutte den redigeringsproces.

Redigering af et brugerobjekt i et array

Med vores basiskomponentsæt, lad os nu tilføje redigeringsfunktionaliteten, som vi antydede ovenfor:

/pages/index.js

import React from "react";
import PropTypes from "prop-types";
import usersFixture from "../lib/users";

class Index extends React.Component {
  state = {
    editingUser: null,
    users: usersFixture,
  };

  renderUserEditor = () => {
    const { editingUser, users } = this.state;
    const user = users.find(({ _id }) => _id === editingUser);

    return (
      <div
        className="edit-user"
        style={{
          border: "1px solid #ddd",
          padding: "20px",
          borderRadius: "3px",
          marginTop: "40px",
          marginBottom: "40px",
        }}
      >
        <form onSubmit={this.handleUpdateUser}>
          <div className="row">
            <div className="col-xs-12 col-sm-3">
              <div className="mb-3">
                <label className="form-label">First Name</label>
                <input
                  type="text"
                  className="form-control"
                  defaultValue={user?.name?.first}
                  name="firstName"
                />
              </div>
            </div>
            <div className="col-xs-12 col-sm-3">
              <div className="mb-3">
                <label className="form-label">Last Name</label>
                <input
                  type="text"
                  className="form-control"
                  defaultValue={user?.name?.last}
                  name="lastName"
                />
              </div>
            </div>
            <div className="col-xs-12 col-sm-6">
              <div className="mb-3">
                <label className="form-label">Email Address</label>
                <input
                  type="text"
                  className="form-control"
                  defaultValue={user?.emailAddress}
                  name="emailAddress"
                />
              </div>
            </div>
          </div>
          <div className="row">
            <div className="col-xs-12 col-sm-5">
              <label className="form-label">Street Address</label>
              <input
                disabled
                type="text"
                className="form-control"
                defaultValue={user?.address?.streetAddress}
                name="streetAddress"
              />
            </div>
            <div className="col-xs-12 col-sm-3">
              <label className="form-label">City</label>
              <input
                disabled
                type="text"
                className="form-control"
                defaultValue={user?.address?.city}
                name="city"
              />
            </div>
            <div className="col-xs-12 col-sm-2">
              <label className="form-label">State</label>
              <input
                disabled
                type="text"
                className="form-control"
                defaultValue={user?.address?.state}
                name="state"
              />
            </div>
            <div className="col-xs-12 col-sm-2">
              <label className="form-label">Zip Code</label>
              <input
                disabled
                type="text"
                className="form-control"
                defaultValue={user?.address?.zipCode}
                name="zipCode"
              />
            </div>
          </div>
          <footer className="mt-4">
            <button type="submit" className="btn btn-success">
              Save
            </button>
            <button
              type="button"
              className="btn btn-default"
              onClick={() => this.setState({ editingUser: null })}
            >
              Cancel
            </button>
          </footer>
        </form>
      </div>
    );
  };

  render() {
    const { editingUser, users } = this.state;

    return (
      <div>
        <header className="page-header">
          <h4>Test</h4>
        </header>
        {editingUser && this.renderUserEditor()}
        <div className="responsive-table">
          <table className="table align-middle">
            <thead>
              <tr>
                <th>Name</th>
                <th>Email Address</th>
                <th>Address</th>
                <th />
              </tr>
            </thead>
            <tbody>
              {users.map(({ _id, name, emailAddress, address }) => { ... })
            </tbody>
          </table>
        </div>
      </div>
    );
  }
}

Index.propTypes = {};

export default Index;

Hvis vi går lidt længere, har vi tilføjet editingUser og indstil den til null på vores standard state objekt øverst i vores komponentklasse. Dernæst i vores render() funktion, har vi tilføjet et opkald til this.renderUserEditor() og har tilføjet i funktionen. Ideen her er, at når vi klikker på knappen "Rediger" for en bruger, indstiller vi deres _id på tilstand (taget fra deres brugerobjekt i users array) og skifter derefter samtidig gengivelsen af ​​brugereditoren og deaktiver alle redigeringsknapper for brugere, indtil brugereditoren er lukket (enten ved at gemme ændringer eller annullere ændringerne).

Forudsat at vi har en editingUser sæt og renderUserEditor() er blevet kaldt, når man ser på den funktion, er den del, vi holder af, toppen:

const { editingUser, users } = this.state;
const user = users.find(({ _id }) => _id === editingUser);

Husk:vi har at gøre med en statisk række af brugere på staten. I stedet for at hente data fra en server, siger vi her "fluk editingUser og users array fra tilstand, og brug derefter en JavaScript .find()users array for at finde den bruger, der har en _id matcher editingUser vi går i gang med tilstanden." Så når vi klikker på en brugers "Rediger"-knap, bliver de user som vi henter her.

Når den er hentet inde i renderUserEditor() , gengiver vi en formular, der vil være ansvarlig for at tillade os at foretage ændringer til den bruger. Her kan vi se, at vores formular – igen ved at bruge Bootstrap CSS til at rydde op i vores præsentation – viser hvert af de felter, der er tilgængelige i brugerobjektet som input med deres defaultValue indstillet til det felts værdi på user objekt. For at gøre tingene enkle, tillader vi kun redigeringer på name.first , name.last og emailAddress for brugeren; de andre felter er deaktiveret.

To ting mere. Først i bunden af ​​renderUserEditor() , returnerer vi en <footer></footer> med to knapper, en "Gem"-knap og en "Annuller"-knap. "Annuller"-knappen her er ansvarlig for at slette editingUser på tilstand, når der klikkes på den (husk, dette skifter gengivelsen af ​​brugereditoren og den deaktiverede tilstand for redigeringsknapperne for brugere på vores liste). Den vigtigere knap, "Gem", er indstillet til en type="submit" , hvilket betyder, at når der klikkes på det, vil det udløse onSubmit hændelse for <form></form> det pakker det ind.

Her kan vi se at <form></form> har en onSubmit indstillet til en funktion this.handleUpdateUser . Lad os koble den funktion op nu og se, hvordan det spiller ind i at ændre vores array.

/pages/index.js

import React from "react";
import PropTypes from "prop-types";
import usersFixture from "../lib/users";

class Index extends React.Component {
  state = {
    editingUser: null,
    users: usersFixture,
  };

  handleUpdateUser = (event) => {
    event.preventDefault();

    const { editingUser, users } = this.state;
    const updatedUsers = [...users];
    let userToUpdate = updatedUsers.find(({ _id }) => _id === editingUser);

    if (userToUpdate) {
      userToUpdate.name = {
        first: event.target.firstName.value,
        last: event.target.lastName.value,
      };

      userToUpdate.emailAddress = event.target.emailAddress.value;
    }

    this.setState({ users: updatedUsers, editingUser: null });
  };

  renderUserEditor = () => {
    const { editingUser, users } = this.state;
    const user = users.find(({ _id }) => _id === editingUser);

    return (
      <div
        className="edit-user"
        style={{
          border: "1px solid #ddd",
          padding: "20px",
          borderRadius: "3px",
          marginTop: "40px",
          marginBottom: "40px",
        }}
      >
        <form onSubmit={this.handleUpdateUser}>
          ...
        </form>
      </div>
    );
  };

  render() {
    const { editingUser, users } = this.state;

    return (
      <div>
        <header className="page-header">
          <h4>Test</h4>
        </header>
        {editingUser && this.renderUserEditor()}
        <div className="responsive-table">
          <table className="table align-middle">
            <thead>
              <tr>
                <th>Name</th>
                <th>Email Address</th>
                <th>Address</th>
                <th />
              </tr>
            </thead>
            <tbody>
              {users.map(({ _id, name, emailAddress, address }) => { ... })}
            </tbody>
          </table>
        </div>
      </div>
    );
  }
}

Index.propTypes = {};

export default Index;

Inde i vores nye handleUpdateUser() funktion, først tager vi indsend event som et argument og straks kalde til dets .preventDefault() metode. Dette er vigtigt, da vi ikke ønsker, at vores indsendelsesbegivenhed udløser en browseropdatering – det stopper det.

Dernæst ser vi noget, der ligner det, vi så i renderUserEditor() . Det er den del, vi holder af i denne vejledning. Her plukker vi editingUser af og users array fra this.state igen. Husk, vores mål er at redigere et objekt, der findes i et array. For at gøre det skal vi vide to ting:

  1. Hvilket array kigger vi i?
  2. Hvordan finder vi det objekt, der skal opdateres i det array?

Her bruger vi _id vi indstiller på editingUser da vi klikkede på knappen "Rediger" ud for en af ​​vores brugere. Nu skal vi igen understrege, at vores mål er at redigere et objekt som det eksisterer i en matrix . I dette eksempel er arrayet vores users array.

Først, før vi "finder" vores bruger, opretter vi en kopi af users array på tilstand (det er uden for denne tutorials omfang, men den gyldne regel i React er, at du ikke ønsker at mutere tilstandsværdier direkte) med [...users] . Her siger vi på én linje "opret et nyt array [] og brug derefter ... spredningsoperatør for at "pakke ud" eller kopiere indholdet af users ind i det nye array." Dette er det array, som vi vil ændre.

Dernæst igen ved at bruge en JavaScript .find() på vores nye updatedUsers array, kører vi den samme test, som vi brugte tidligere til at sige "find os en bruger med en _id der matcher editingUser ." Forudsat at vi gør finder den bruger, begynder vi at foretage ændringer i dem. Her indstiller vi name objektet og emailAddress feltet på userToUpdate .

Selvom det måske ikke ser sådan ud, fordi vi brugte en JavaScript .find() her er vi faktisk ved at ændre den matchende userToUpdate objekt, som det findes i updatedUsers række i hukommelsen. Hvad dette betyder er, at selvom vores kode foretager ændringer til userToUpdate , i sidste ende foretager den ændringer til updatedUsers .

Når disse ændringer er gennemført (her indstiller vi blot de førnævnte værdier til de tilsvarende input i vores formular ved hjælp af event.target.<fieldName>.value hvor <fieldName> er name="" attribut på input), overskriver vi vores users værdi på tilstand med this.setState() , og sender vores updatedUsers array.

Slutresultatet? Vi vil se vores users listeopdatering i vores tabel, hvilket med succes viser, at vi har opdateret et objekt inde i et array.

Afslutning

I dette selvstudie lærte vi, hvordan man ændrer et objekt i et JavaScript-array. For at kontekstualisere vores arbejde byggede vi en React-komponent, der ændrede en liste over brugere i et array på tilstand og derefter satte den tilbage på tilstand, hvilket gengiver den opdaterede liste i en HTML-tabel.