การเปลี่ยน state ของ parent ใน child component ใน React
ในบทเรียนก่อนหน้านี้ state ที่เก็บข้อมูลของเรา ถูกเก็บไว้ใน component หลัก (parent) ในขณะที่ component ย่อย (child) ได้รับข้อมูลเหล่านั้นผ่าน props
สมมติว่าตอนนี้เราต้องการเปลี่ยนแปลงข้อมูลสินค้าของเรา
เรามาสร้างปุ่ม เช่น ปุ่มที่จะ
นำสินค้าของเราใส่ลงในตะกร้าสินค้า ก่อนอื่น
เรามาเพิ่มฟิลด์ inCart ในอาร์เรย์สินค้าของเรา
เพื่อแสดงว่าสินค้าอยู่ในตะกร้าหรือไม่:
const initProds = [
{id: id(), name: 'product1', cost: 100, inCart: false},
{id: id(), name: 'product2', cost: 200, inCart: false},
{id: id(), name: 'product3', cost: 300, inCart: false},
];
ใน component Products ในแท็กที่แสดงสินค้า
ให้เพิ่มแอตทริบิวต์ inCart อีกหนึ่ง:
function Products() {
const [prods, setProds] = useState(initProds);
const items = prods.map(prod => {
return <Product
key ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart={prod.inCart}
/>;
});
return <div>
{items}
</div>;
}
มาแสดงข้อมูลเกี่ยวกับตะกร้าและปุ่ม
สำหรับเพิ่มลงในตะกร้าใน component ย่อย Product:
function Product({ id, name, cost, inCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'in cart' : 'not in cart'}</span>
<button>to cart</button>
</div>;
}
การปรับใช้ฟังก์ชันเพิ่ม
ตามกฎของ React component ไม่ควร
เปลี่ยนแปลง props ของตัวเอง นั่นหมายความว่า
component ย่อยไม่สามารถเพิ่มตัวเอง
ลงในตะกร้าได้โดยการเปลี่ยน prop inCart
นั่นไม่ถูกต้อง
วิธีที่ถูกต้องคือขอให้ component หลัก
เปลี่ยน state prods ของตัวเอง โดยเพิ่ม
สินค้าที่กำหนดลงในตะกร้า
มาดูกันว่าทำได้อย่างไร
ใน component หลัก สร้างฟังก์ชัน addToCart,
ซึ่งรับพารามิเตอร์เป็น id ของสินค้า
และสำหรับสินค้านั้นเปลี่ยนคุณสมบัติ inCart
เป็น true:
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
ในแท็กสินค้า ให้เพิ่มแอตทริบิวต์ซึ่ง
ส่งผ่านฟังก์ชันที่เราสร้าง และยัง
เพิ่มแอตทริบิวต์ซึ่งส่งผ่าน id ของสินค้า:
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
อย่างที่คุณเห็น ใน props ของ component สามารถ ส่งผ่านได้ไม่เพียงแค่ข้อมูลบางอย่าง แต่ รวมถึงฟังก์ชันด้วย
โค้ดสุดท้ายของคลาส Products จะได้
ดังนี้:
function Products() {
const [prods, setProds] = useState(initProds);
function addToCart(id) {
setProds(prods.map(prod => {
if (prod.id === id) {
prod.inCart = true;
}
return prod;
}));
}
const items = prods.map(prod => {
return <Product
key ={prod.id}
id ={prod.id}
name ={prod.name}
cost ={prod.cost}
inCart ={prod.inCart}
addToCart={addToCart}
/>;
});
return <div>
{items}
</div>;
}
ตอนนี้ในคลาสย่อย เราจะสามารถเข้าถึง
ฟังก์ชัน addToCart ได้ เรียกใช้ฟังก์ชันนี้
เมื่อคลิกปุ่ม โดยส่งพารามิเตอร์เป็น
id ของสินค้า:
function Product({ id, name, cost, inCart, addToCart }) {
return <div>
name: <span>{name}</span>,
cost: <span>{cost}</span>,
<span>{inCart ? 'in cart' : 'not in cart'}</span>
<button onClick={() => addToCart(id)}>to cart</button>
</div>;
}
ผลลัพธ์คือ เมื่อคลิกปุ่มใน component ลูก จะเรียกใช้ฟังก์ชันของ component หลัก ซึ่งจะเปลี่ยน state ของ component หลัก การเปลี่ยน state ของ component หลัก จะทำให้เกิดการเรนเดอร์ใหม่และวาด สินค้าของเราใหม่ โดยส่ง prop ที่เปลี่ยนแปลงแล้วให้กับมัน
นำ component User จากบทเรียนก่อนหน้า
มาทำให้มีปุ่มสำหรับแบนผู้ใช้