ปลดล็อกพลังของลอจิกที่นำกลับมาใช้ใหม่ได้ในแอปพลิเคชัน React ของคุณด้วย Custom Hooks เรียนรู้วิธีสร้างและใช้ Custom Hooks เพื่อโค้ดที่สะอาดและดูแลรักษาง่ายขึ้น
Custom Hooks: รูปแบบลอจิกที่นำกลับมาใช้ใหม่ได้ใน React
React Hooks ได้ปฏิวัติวิธีการเขียนคอมโพเนนต์ React โดยการนำเสนอ state และฟีเจอร์ lifecycle ให้กับ functional components ในบรรดาประโยชน์มากมายที่พวกเขามอบให้ Custom Hooks โดดเด่นในฐานะกลไกอันทรงพลังสำหรับการดึงและนำลอจิกกลับมาใช้ใหม่ในคอมโพเนนต์ต่างๆ บล็อกโพสต์นี้จะเจาะลึกเข้าไปในโลกของ Custom Hooks สำรวจประโยชน์ การสร้าง และการใช้งานพร้อมตัวอย่างที่นำไปใช้ได้จริง
Custom Hooks คืออะไร?
โดยพื้นฐานแล้ว Custom Hook คือฟังก์ชัน JavaScript ที่ขึ้นต้นด้วยคำว่า "use" และสามารถเรียกใช้ Hooks อื่นๆ ได้ มันช่วยให้คุณสามารถดึงลอจิกของคอมโพเนนต์ออกมาเป็นฟังก์ชันที่นำกลับมาใช้ใหม่ได้ นี่เป็นวิธีที่ทรงพลังในการแบ่งปัน stateful logic, side effects หรือพฤติกรรมที่ซับซ้อนอื่นๆ ระหว่างคอมโพเนนต์โดยไม่ต้องใช้ render props, higher-order components หรือรูปแบบที่ซับซ้อนอื่นๆ
ลักษณะสำคัญของ Custom Hooks:
- ธรรมเนียมการตั้งชื่อ: Custom Hooks ต้องขึ้นต้นด้วยคำว่า "use" สิ่งนี้เป็นการส่งสัญญาณให้ React รู้ว่าฟังก์ชันนั้นมี Hooks อยู่และควรปฏิบัติตามกฎของ Hooks
- การนำกลับมาใช้ใหม่ได้: จุดประสงค์หลักคือการห่อหุ้มลอจิกที่นำกลับมาใช้ใหม่ได้ ทำให้ง่ายต่อการแบ่งปันฟังก์ชันการทำงานระหว่างคอมโพเนนต์
- Stateful Logic: Custom Hooks สามารถจัดการ state ของตัวเองได้โดยใช้
useState
hook ทำให้สามารถห่อหุ้มพฤติกรรมที่มีสถานะซับซ้อนได้ - Side Effects: พวกมันยังสามารถทำงานที่เป็น side effects ได้โดยใช้
useEffect
hook ทำให้สามารถทำงานร่วมกับ API ภายนอก, การดึงข้อมูล และอื่นๆ ได้ - การประกอบกันได้: Custom Hooks สามารถเรียกใช้ Hooks อื่นๆ ได้ ทำให้คุณสามารถสร้างลอจิกที่ซับซ้อนโดยการประกอบ Hooks ที่เล็กกว่าและเฉพาะทางเข้าด้วยกัน
ประโยชน์ของการใช้ Custom Hooks
Custom Hooks มอบข้อได้เปรียบที่สำคัญหลายประการในการพัฒนา React:
- การนำโค้ดกลับมาใช้ใหม่: ประโยชน์ที่ชัดเจนที่สุดคือความสามารถในการนำลอจิกกลับมาใช้ใหม่ในหลายคอมโพเนนต์ ซึ่งช่วยลดการทำซ้ำของโค้ดและส่งเสริม codebase ที่เป็นแบบ DRY (Don't Repeat Yourself) มากขึ้น
- ปรับปรุงความสามารถในการอ่าน: ด้วยการดึงลอจิกที่ซับซ้อนออกมาไว้ใน Custom Hooks ที่แยกจากกัน คอมโพเนนต์ของคุณจะสะอาดและเข้าใจง่ายขึ้น ลอจิกหลักของคอมโพเนนต์จะยังคงมุ่งเน้นไปที่การเรนเดอร์ UI
- เพิ่มความสามารถในการบำรุงรักษา: เมื่อลอจิกถูกห่อหุ้มไว้ใน Custom Hooks การเปลี่ยนแปลงและการแก้ไขข้อบกพร่องสามารถทำได้ในที่เดียว ซึ่งช่วยลดความเสี่ยงในการเกิดข้อผิดพลาดในหลายคอมโพเนนต์
- ความสามารถในการทดสอบ: Custom Hooks สามารถทดสอบแบบแยกส่วนได้อย่างง่ายดาย ทำให้มั่นใจได้ว่าลอจิกที่นำกลับมาใช้ใหม่นั้นทำงานได้อย่างถูกต้องโดยไม่ขึ้นอยู่กับคอมโพเนนต์ที่ใช้งาน
- คอมโพเนนต์ที่เรียบง่ายขึ้น: Custom Hooks ช่วยลดความซับซ้อนในคอมโพเนนต์ ทำให้มีความกระชับและมุ่งเน้นไปที่วัตถุประสงค์หลักมากขึ้น
การสร้าง Custom Hook แรกของคุณ
เรามาสาธิตการสร้าง Custom Hook ด้วยตัวอย่างที่ใช้งานได้จริง: hook ที่ติดตามขนาดของหน้าต่าง
ตัวอย่าง: useWindowSize
hook นี้จะคืนค่าความกว้างและความสูงปัจจุบันของหน้าต่างเบราว์เซอร์ และจะอัปเดตค่าเหล่านี้เมื่อหน้าต่างถูกปรับขนาด
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// ลบ event listener ออกเมื่อทำการ cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // อาร์เรย์ว่างเปล่าเพื่อให้แน่ใจว่า effect จะทำงานเฉพาะตอน mount เท่านั้น
return windowSize;
}
export default useWindowSize;
คำอธิบาย:
- นำเข้า Hooks ที่จำเป็น: เรานำเข้า
useState
และuseEffect
จาก React - กำหนด Hook: เราสร้างฟังก์ชันชื่อ
useWindowSize
โดยยึดตามธรรมเนียมการตั้งชื่อ - เริ่มต้น State: เราใช้
useState
เพื่อเริ่มต้น statewindowSize
ด้วยความกว้างและความสูงเริ่มต้นของหน้าต่าง - ตั้งค่า Event Listener: เราใช้
useEffect
เพื่อเพิ่ม resize event listener ไปยังหน้าต่าง เมื่อหน้าต่างถูกปรับขนาด ฟังก์ชันhandleResize
จะอัปเดต statewindowSize
- Cleanup: เราคืนค่าฟังก์ชัน cleanup จาก
useEffect
เพื่อลบ event listener เมื่อคอมโพเนนต์ unmount ซึ่งจะช่วยป้องกันหน่วยความจำรั่วไหล (memory leaks) - คืนค่า: hook จะคืนค่าอ็อบเจกต์
windowSize
ซึ่งประกอบด้วยความกว้างและความสูงปัจจุบันของหน้าต่าง
การใช้ Custom Hook ในคอมโพเนนต์
เมื่อเราสร้าง Custom Hook ของเราแล้ว มาดูกันว่าจะใช้งานในคอมโพเนนต์ React ได้อย่างไร
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
ความกว้างหน้าต่าง: {width}px
ความสูงหน้าต่าง: {height}px
);
}
export default MyComponent;
คำอธิบาย:
- นำเข้า Hook: เรานำเข้า Custom Hook
useWindowSize
- เรียกใช้ Hook: เราเรียกใช้ hook
useWindowSize
ภายในคอมโพเนนต์ - เข้าถึงค่า: เราใช้ destructuring กับอ็อบเจกต์ที่ส่งคืนมาเพื่อรับค่า
width
และheight
- แสดงผลค่า: เราแสดงผลค่าความกว้างและความสูงใน UI ของคอมโพเนนต์
คอมโพเนนต์ใดๆ ที่ใช้ useWindowSize
จะอัปเดตโดยอัตโนมัติเมื่อขนาดของหน้าต่างเปลี่ยนแปลง
ตัวอย่างที่ซับซ้อนยิ่งขึ้น
เรามาสำรวจกรณีการใช้งานขั้นสูงสำหรับ Custom Hooks กัน
ตัวอย่าง: useLocalStorage
hook นี้ช่วยให้คุณสามารถจัดเก็บและดึงข้อมูลจาก local storage ได้อย่างง่ายดาย
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State สำหรับเก็บค่าของเรา
// ส่งค่าเริ่มต้นไปยัง useState เพื่อให้ลอจิกทำงานเพียงครั้งเดียว
const [storedValue, setStoredValue] = useState(() => {
try {
// ดึงข้อมูลจาก local storage ด้วย key
const item = window.localStorage.getItem(key);
// แปลง JSON ที่เก็บไว้ หรือถ้าไม่มีให้คืนค่า initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// หากเกิดข้อผิดพลาดก็ให้คืนค่า initialValue เช่นกัน
console.log(error);
return initialValue;
}
});
// คืนค่าฟังก์ชัน setter ของ useState ในเวอร์ชันที่ห่อหุ้มไว้ ซึ่ง...
// ...จะบันทึกค่าใหม่ลงใน localStorage
const setValue = (value) => {
try {
// อนุญาตให้ value เป็นฟังก์ชันเพื่อให้เรามี API เหมือนกับ useState
const valueToStore = value instanceof Function ? value(storedValue) : value;
// บันทึกลงใน local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// บันทึก state
setStoredValue(valueToStore);
} catch (error) {
// การใช้งานขั้นสูงกว่านี้จะมีการจัดการกรณีเกิดข้อผิดพลาด
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
การใช้งาน:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Guest');
return (
สวัสดี, {name}!
setName(e.target.value)}
/>
);
}
export default MyComponent;
ตัวอย่าง: useFetch
hook นี้จะห่อหุ้มลอจิกสำหรับการดึงข้อมูลจาก API
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
การใช้งาน:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return กำลังโหลด...
;
if (error) return ข้อผิดพลาด: {error.message}
;
return (
หัวข้อ: {data.title}
เสร็จสิ้น: {data.completed ? 'ใช่' : 'ไม่'}
);
}
export default MyComponent;
แนวทางปฏิบัติที่ดีที่สุดสำหรับ Custom Hooks
เพื่อให้แน่ใจว่า Custom Hooks ของคุณมีประสิทธิภาพและสามารถบำรุงรักษาได้ ควรปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้:
- ทำให้มีจุดประสงค์เดียว: Custom Hook แต่ละตัวควรมีจุดประสงค์เดียวที่กำหนดไว้อย่างชัดเจน หลีกเลี่ยงการสร้าง Hooks ที่ซับซ้อนเกินไปซึ่งพยายามทำหลายอย่างมากเกินไป
- จัดทำเอกสารสำหรับ Hooks ของคุณ: จัดทำเอกสารที่ชัดเจนและรัดกุมสำหรับ Custom Hook แต่ละตัว โดยอธิบายวัตถุประสงค์, input และ output
- ทดสอบ Hooks ของคุณ: เขียน unit tests สำหรับ Custom Hooks ของคุณเพื่อให้แน่ใจว่าทำงานได้อย่างถูกต้องและน่าเชื่อถือ
- ใช้ชื่อที่สื่อความหมาย: เลือกชื่อที่สื่อความหมายสำหรับ Custom Hooks ของคุณซึ่งบ่งบอกถึงวัตถุประสงค์อย่างชัดเจน
- จัดการข้อผิดพลาดอย่างเหมาะสม: ใช้งานการจัดการข้อผิดพลาดภายใน Custom Hooks ของคุณเพื่อป้องกันพฤติกรรมที่ไม่คาดคิดและให้ข้อความแสดงข้อผิดพลาดที่เป็นประโยชน์
- พิจารณาการนำกลับมาใช้ใหม่: ออกแบบ Custom Hooks โดยคำนึงถึงการนำกลับมาใช้ใหม่ ทำให้มันเป็นแบบทั่วไปเพียงพอที่จะใช้ได้ในหลายๆ คอมโพเนนต์
- หลีกเลี่ยงการสร้าง Abstraction มากเกินไป: อย่าสร้าง Custom Hooks สำหรับลอจิกง่ายๆ ที่สามารถจัดการได้ง่ายภายในคอมโพเนนต์ ควรดึงเฉพาะลอจิกที่นำกลับมาใช้ใหม่ได้และซับซ้อนจริงๆ เท่านั้น
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง
- การละเมิดกฎของ Hooks: เรียกใช้ Hooks ที่ระดับบนสุดของฟังก์ชัน Custom Hook ของคุณเสมอ และเรียกใช้จาก React function components หรือ Custom Hooks อื่นๆ เท่านั้น
- การละเลย Dependencies ใน useEffect: ตรวจสอบให้แน่ใจว่าได้รวม dependencies ที่จำเป็นทั้งหมดไว้ใน dependency array ของ
useEffect
hook เพื่อป้องกัน stale closures และพฤติกรรมที่ไม่คาดคิด - การสร้าง Infinite Loops: ระมัดระวังเมื่ออัปเดต state ภายใน
useEffect
hook เนื่องจากอาจนำไปสู่ infinite loops ได้ง่าย ตรวจสอบให้แน่ใจว่าการอัปเดตเป็นแบบมีเงื่อนไขและขึ้นอยู่กับการเปลี่ยนแปลงของ dependencies - การลืม Cleanup: ใส่ฟังก์ชัน cleanup ใน
useEffect
เสมอเพื่อลบ event listeners, ยกเลิก subscriptions และทำงาน cleanup อื่นๆ เพื่อป้องกันหน่วยความจำรั่วไหล (memory leaks)
รูปแบบขั้นสูง
การประกอบ Custom Hooks เข้าด้วยกัน
Custom Hooks สามารถนำมาประกอบเข้าด้วยกันเพื่อสร้างลอจิกที่ซับซ้อนมากขึ้น ตัวอย่างเช่น คุณสามารถรวม useLocalStorage
hook กับ useFetch
hook เพื่อบันทึกข้อมูลที่ดึงมาลงใน local storage โดยอัตโนมัติ
การแบ่งปันลอจิกระหว่าง Hooks
หาก Custom Hooks หลายตัวมีลอจิกร่วมกัน คุณสามารถดึงลอจิกนั้นออกมาเป็นฟังก์ชัน utility ที่แยกต่างหากและนำกลับมาใช้ในทั้งสอง hooks ได้
การใช้ Context กับ Custom Hooks
Custom Hooks สามารถใช้ร่วมกับ React Context เพื่อเข้าถึงและอัปเดต global state ได้ ซึ่งช่วยให้คุณสร้างคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้ซึ่งรับรู้และสามารถโต้ตอบกับ global state ของแอปพลิเคชันได้
ตัวอย่างในโลกแห่งความเป็นจริง
นี่คือตัวอย่างบางส่วนของวิธีที่ Custom Hooks สามารถนำไปใช้ในแอปพลิเคชันในโลกแห่งความเป็นจริงได้:
- การตรวจสอบความถูกต้องของฟอร์ม (Form Validation): สร้าง
useForm
hook เพื่อจัดการ state, การตรวจสอบความถูกต้อง และการส่งฟอร์ม - การยืนยันตัวตน (Authentication): สร้าง
useAuth
hook เพื่อจัดการการยืนยันตัวตนและการให้สิทธิ์ผู้ใช้ - การจัดการธีม (Theme Management): พัฒนา
useTheme
hook เพื่อสลับระหว่างธีมต่างๆ (สว่าง, มืด ฯลฯ) - ตำแหน่งทางภูมิศาสตร์ (Geolocation): สร้าง
useGeolocation
hook เพื่อติดตามตำแหน่งปัจจุบันของผู้ใช้ - การตรวจจับการเลื่อน (Scroll Detection): สร้าง
useScroll
hook เพื่อตรวจจับเมื่อผู้ใช้เลื่อนไปยังจุดที่กำหนดบนหน้าเว็บ
ตัวอย่าง : useGeolocation hook สำหรับแอปพลิเคชันข้ามวัฒนธรรม เช่น แอปแผนที่หรือบริการจัดส่ง
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'เบราว์เซอร์นี้ไม่รองรับ Geolocation',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
สรุป
Custom Hooks เป็นเครื่องมืออันทรงพลังสำหรับการเขียนโค้ด React ที่สะอาดขึ้น, นำกลับมาใช้ใหม่ได้มากขึ้น และบำรุงรักษาง่ายขึ้น ด้วยการห่อหุ้มลอจิกที่ซับซ้อนไว้ใน Custom Hooks คุณสามารถทำให้คอมโพเนนต์ของคุณเรียบง่ายขึ้น ลดการทำซ้ำของโค้ด และปรับปรุงโครงสร้างโดยรวมของแอปพลิเคชันของคุณได้ จงใช้ Custom Hooks และปลดล็อกศักยภาพในการสร้างแอปพลิเคชัน React ที่แข็งแกร่งและขยายขนาดได้
เริ่มต้นด้วยการระบุส่วนต่างๆ ใน codebase ที่มีอยู่ของคุณซึ่งมีการใช้ลอจิกซ้ำๆ ในหลายคอมโพเนนต์ จากนั้นทำการ refactor ลอจิกนั้นให้เป็น Custom Hooks เมื่อเวลาผ่านไป คุณจะสร้างคลังของ hooks ที่นำกลับมาใช้ใหม่ได้ซึ่งจะช่วยเร่งกระบวนการพัฒนาและปรับปรุงคุณภาพโค้ดของคุณ
อย่าลืมปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุด หลีกเลี่ยงข้อผิดพลาดทั่วไป และสำรวจรูปแบบขั้นสูงเพื่อใช้ประโยชน์สูงสุดจาก Custom Hooks ด้วยการฝึกฝนและประสบการณ์ คุณจะกลายเป็นผู้เชี่ยวชาญด้าน Custom Hooks และเป็นนักพัฒนา React ที่มีประสิทธิภาพยิ่งขึ้น