State คืออะไร? การจัดการ State ใน Component
สวัสดีครับเพื่อนๆ นักพัฒนา React Native ทุกท่าน! ในบทความที่แล้ว เราได้ทำความเข้าใจเกี่ยวกับ Props ซึ่งเป็นข้อมูลที่ส่งจาก Component หนึ่งไปยังอีก Component หนึ่ง คล้ายกับการส่งของขวัญจากพ่อแม่ให้ลูก แต่ในบทความนี้ เราจะมาพูดถึง State ซึ่งเป็นข้อมูลที่ Component แต่ละตัว "เป็นเจ้าของ" และจัดการเอง คล้ายกับของเล่นที่ลูกมีและสามารถเล่นกับมันได้ตามใจชอบ ถ้าพร้อมแล้ว เราไปดูกันเลย!
State คืออะไร?
State ใน React Native (และ React) คือข้อมูลภายใน Component ที่สามารถเปลี่ยนแปลงได้ เมื่อ State เปลี่ยนแปลง Component จะทำการ Re-rendering หรือวาดใหม่ เพื่อแสดงผลลัพธ์ที่อัพเดตตาม State ที่เปลี่ยนไป ลองนึกภาพว่าคุณมี Component ที่แสดงจำนวนครั้งที่กดปุ่ม ทุกครั้งที่คุณกดปุ่ม จำนวนครั้งที่แสดงบนหน้าจอจะเพิ่มขึ้น ข้อมูลจำนวนครั้งนี้แหละครับ คือ State
State ต่างจาก Props ตรงที่ Props เป็นข้อมูลที่มาจากภายนอก (Parent Component) และอ่านได้อย่างเดียว (read-only) ส่วน State เป็นข้อมูลภายใน Component ที่สามารถเปลี่ยนแปลงได้ และการเปลี่ยนแปลง State จะทำให้ Component อัพเดตตัวเอง
การใช้ useState Hook
ใน React Native (และ React ตั้งแต่เวอร์ชัน 16.8 เป็นต้นไป) เราใช้สิ่งที่เรียกว่า Hook เพื่อจัดการ State Hook เป็นฟังก์ชันพิเศษที่ช่วยให้เรา "Hook" เข้าไปในฟีเจอร์ของ React โดยไม่ต้องเขียน Class Component หนึ่งใน Hook ที่สำคัญที่สุดคือ useState
useState Hook ช่วยให้เราเพิ่ม State ลงใน Functional Component ได้ วิธีการใช้งานง่ายมากครับ
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const MyComponent = () => {
// ประกาศ State โดยใช้ useState Hook
const [count, setCount] = useState(0);
return (
<View>
<Text>You clicked {count} times</Text>
<Button
title="Click me"
onPress={() => setCount(count + 1)}
/>
</View>
);
};
export default MyComponent;
มาดูกันทีละส่วนครับ
import React, { useState } from 'react';: บรรทัดนี้เรา import React และuseStateHook เข้ามาใน Componentconst [count, setCount] = useState(0);: ตรงนี้คือหัวใจสำคัญ เราเรียกuseState(0)ซึ่งหมายความว่าเราสร้าง State ชื่อcountโดยมีค่าเริ่มต้นเป็น0useStateจะคืนค่ากลับมาเป็น Array ที่มีสมาชิก 2 ตัว:count: ตัวแปรที่เก็บค่า State ปัจจุบันsetCount: ฟังก์ชันที่เราใช้เพื่ออัพเดตค่า State
<Text>You clicked {count} times</Text>: เราแสดงค่า Statecountใน Text Component<Button title="Click me" onPress={() => setCount(count + 1)} />: เมื่อกดปุ่ม "Click me" ฟังก์ชันsetCount(count + 1)จะถูกเรียก ซึ่งจะอัพเดตค่า Statecountโดยเพิ่มค่าขึ้นทีละ 1
สังเกตว่าเมื่อเราเรียก setCount(count + 1) Component MyComponent จะทำการ Re-rendering ทำให้ค่า count ที่แสดงบนหน้าจออัพเดตตามไปด้วย
การเปลี่ยนแปลง State และ Re-rendering
เมื่อ State เปลี่ยนแปลง React Native จะทำการ Re-rendering Component นั้นๆ เพื่อให้ UI อัพเดตตาม State ใหม่ กระบวนการนี้เกิดขึ้นโดยอัตโนมัติเมื่อเราเรียกฟังก์ชันที่ได้จากการเรียก useState (เช่น setCount ในตัวอย่างก่อนหน้า)
**ข้อควรจำ:** ห้ามเปลี่ยนแปลง State โดยตรง! เช่น count = count + 1; เพราะ React Native จะไม่สามารถตรวจจับการเปลี่ยนแปลงนี้ได้ และ Component จะไม่ Re-render เสมอไป เราต้องใช้ฟังก์ชันที่ได้จาก useState เสมอ (เช่น setCount) เพื่อให้ React Native รู้ว่า State มีการเปลี่ยนแปลง
ลองดูตัวอย่างที่ไม่ถูกต้อง:
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
// ผิด! ห้ามทำแบบนี้
count = count + 1;
console.log(count); // ค่า count จะเปลี่ยนใน console แต่ UI ไม่เปลี่ยน
};
return (
<View>
<Text>You clicked {count} times</Text>
<Button
title="Click me"
onPress={handleClick}
/>
</View>
);
};
export default MyComponent;
ในตัวอย่างนี้ แม้ว่าค่า count จะเปลี่ยนใน console.log แต่ UI จะไม่เปลี่ยนแปลง เพราะเราไม่ได้ใช้ setCount เพื่ออัพเดต State
State ที่ซับซ้อน: Objects และ Arrays
State ไม่ได้จำกัดอยู่แค่ตัวเลขหรือข้อความ เราสามารถใช้ State เก็บ Objects และ Arrays ได้เช่นกัน
**การจัดการ State ที่เป็น Object:**
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
const MyComponent = () => {
const [person, setPerson] = useState({
name: 'John Doe',
age: 30,
});
const updateName = () => {
// สำคัญ: ต้องสร้าง Object ใหม่ทุกครั้ง
setPerson({
...person, // คัดลอก Properties เดิม
name: 'Jane Doe', // เปลี่ยนเฉพาะ name
});
};
return (
<View>
<Text>Name: {person.name}</Text>
<Text>Age: {person.age}</Text>
<Button
title="Update Name"
onPress={updateName}
/>
</View>
);
};
export default MyComponent;
เมื่อเราต้องการอัพเดต State ที่เป็น Object สิ่งสำคัญคือต้องสร้าง Object ใหม่ทุกครั้ง โดยใช้ Spread Operator (...) เพื่อคัดลอก Properties เดิมของ Object แล้วค่อยแก้ไข Property ที่เราต้องการเปลี่ยนแปลง การทำแบบนี้จะทำให้ React Native รู้ว่า Object มีการเปลี่ยนแปลงจริงๆ
**การจัดการ State ที่เป็น Array:**
import React, { useState } from 'react';
import { View, Text, Button, FlatList } from 'react-native';
const MyComponent = () => {
const [items, setItems] = useState(['Apple', 'Banana', 'Orange']);
const addItem = () => {
// สำคัญ: ต้องสร้าง Array ใหม่ทุกครั้ง
setItems([...items, 'Grapes']);
};
return (
<View>
<FlatList
data={items}
renderItem={({ item }) => <Text>{item}</Text>}
keyExtractor={(item) => item}
/>
<Button
title="Add Item"
onPress={addItem}
/>
</View>
);
};
export default MyComponent;
เช่นเดียวกับการจัดการ Object เมื่อเราต้องการอัพเดต State ที่เป็น Array เราต้องสร้าง Array ใหม่ทุกครั้ง โดยใช้ Spread Operator (...) เพื่อคัดลอก Elements เดิมของ Array แล้วค่อยเพิ่มหรือแก้ไข Elements ที่เราต้องการเปลี่ยนแปลง
Performance Considerations
การเปลี่ยนแปลง State บ่อยๆ อาจส่งผลต่อ Performance ของแอปได้ โดยเฉพาะอย่างยิ่งถ้า Component มีขนาดใหญ่และซับซ้อน ดังนั้นเราควรพิจารณาถึง Performance เมื่อจัดการ State
- Memoization: ใช้
useMemoและuseCallbackHooks เพื่อป้องกันการ Re-render Component ที่ไม่จำเป็น - Immutable Data Structures: ใช้ Immutable Data Structures (เช่น Immutable.js) เพื่อให้การตรวจจับการเปลี่ยนแปลง State เป็นไปอย่างมีประสิทธิภาพ
สรุป
State เป็นแนวคิดที่สำคัญมากในการพัฒนา React Native ช่วยให้เราสร้าง Component ที่มีการเปลี่ยนแปลงและตอบสนองต่อการกระทำของผู้ใช้ได้ การใช้ useState Hook ทำให้การจัดการ State ใน Functional Component เป็นเรื่องง่าย แต่ก็ต้องระมัดระวังในการอัพเดต State โดยเฉพาะอย่างยิ่ง State ที่เป็น Object หรือ Array เพื่อให้แน่ใจว่า React Native สามารถตรวจจับการเปลี่ยนแปลงได้อย่างถูกต้องและมีประสิทธิภาพ
ในบทความถัดไป เราจะมาเรียนรู้เกี่ยวกับการใช้ StyleSheet เพื่อตกแต่ง Component ให้สวยงาม น่าใช้งานมากยิ่งขึ้น แล้วพบกันใหม่ครับ!