今天要學習如何在 state 中操作陣列的更新,在 JavaScript 當中陣列是 mutable,但當我們在 React 中處理 array 時應該將它們視為 immutable。所以當我們要更新時,需要創建新的陣列或是複製一個舊的陣列的 copy,再透過 set function 來使用 array。
由於在 React 當中 array 應該當作 immutable 來對待,因此在 JavaScript 學習過的方法有些不建議使用:
避免使用(改動到原始的 array) | 建議使用 (return 新 array) | |
---|---|---|
追加(adding) | push, unshift | concat, [...arr] spread syntax |
移除(removing) | pop, shift, splice | filter, slice |
替代(replacing) | splice, arr[i] = ... assignment | map |
分類(sorting) | reverse, sort | copy the array first |
*但如果使用 Immer 則二邊都行,Immer 後續有機會再介紹了。
接著來看一些應用例,雖然是不同的應用,但原則基本上都是處理好新的 array,再使用 set function 來觸發渲染。
這是一個可追加項目的清單,透過在 input 輸入來新增項目。
import { useState } from "react";
let nextId = 0;
export default function List() {
const [name, setName] = useState("");
const [artists, setArtists] = useState([]);
return (
<>
<h1>台灣藝術家</h1>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button
onClick={() => {
setArtists([...artists, { id: nextId++, name: name }]);
}}>
Add
</button>
<ul>
{artists.map((artist) => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
<button
onClick={() => {
setArtists([...artists, { id: nextId++, name: name }]);
}}>
Add
</button>
透過展開的 spread 用法複製了一份舊的藝術家名單,並將新的藝術家資料添加到這份複製的名單中。這樣做可以確保我們不直接修改原始的 artists
array,而是創建了一個新的陣列,往新的陣列中添加了新的藝術家資料,這樣可以確保 React 正確地檢測到狀態的變化並進行重新渲染。
接著我們想要按按鈕來移除藝術家。
import { useState } from "react";
let initialArtists = [
{ id: 0, name: "黃土水" },
{ id: 1, name: "李石樵" },
{ id: 2, name: "洪瑞麟" },
];
export default function List() {
const [artists, setArtists] = useState(initialArtists);
return (
<>
<h1>Inspiring sculptors:</h1>
<ul>
{artists.map((artist) => (
<li key={artist.id}>
{artist.name}{" "}
<button
onClick={() => {
setArtists(artists.filter((a) => a.id !== artist.id));
}}>
Delete
</button>
</li>
))}
</ul>
</>
);
}
讓我們消化一下這邊的邏輯,也就是過濾出不等於當前藝術家(被點擊刪除的那一位)的所有藝術家後再使用 setArtist 來觸發渲染即完成。
這邊要做的是點擊按鈕後讓圓點點的位置向下位移。
import { useState } from "react";
let initialShapes = [
{ id: 0, type: "circle", x: 50, y: 100 },
{ id: 1, type: "square", x: 150, y: 100 },
{ id: 2, type: "circle", x: 250, y: 100 },
];
export default function ShapeEditor() {
const [shapes, setShapes] = useState(initialShapes);
function handleClick() {
const nextShapes = shapes.map((shape) => {
if (shape.type === "square") {
// 不改變
return shape;
} else {
// 回傳新的圓點點,並且下降50px
return {
...shape,
y: shape.y + 50,
};
}
});
// 再次渲染
setShapes(nextShapes);
}
return (
<>
<button onClick={handleClick}>Move circles down!</button>
{shapes.map((shape) => (
<div
key={shape.id}
style={{
background: "purple",
position: "absolute",
left: shape.x,
top: shape.y,
borderRadius: shape.type === "circle" ? "50%" : "",
width: 20,
height: 20,
}}
/>
))}
</>
);
}
一樣先來看邏輯,將邏輯編寫在handleClick
function 當中,handleClick function 中做二件事,一個是變數用來儲存 nextShapes 的資料,一個是以 setter function 傳入 nextShapes 的資料用來渲染。這邊使用 if else statement 來操作,「若形狀類別為正方形則不變,否則回傳向下位移的新圓點點」,之後再編輯 setter function 這樣就完成了。
以上是今天的學習。今天做了一些陣列的操作,主要的操作方法還是建立在熟悉 JavaScript 的陣列操作上,官方文件也有舉更多的陣列操作例子可以參考看看。
明天要來學習聲明式的思考及操作。