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()
på 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:
- Hvilket array kigger vi i?
- 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.