ฮุก useMemo ใน React Native จดจำผลลัพธ์ของการคำนวณระหว่างการเรนเดอร์คอมโพเนนต์ ป้องกันการดำเนินการซ้ำของการทำงานที่ใช้ทรัพยากรสูง แตกต่างจาก useCallback ที่จดจำฟังก์ชัน useMemo จะเก็บค่าที่ส่งคืนและคำนวณใหม่เมื่อดีเพนเดนซีที่ระบุเปลี่ยนแปลงเท่านั้น ตาม React Docs, 2024 การจดจำค่ามีประสิทธิภาพเป็นพิเศษสำหรับการคำนวณกับอาร์เรย์ขนาดใหญ่และการแปลงข้อมูลที่ซับซ้อน ใน React Native การเรนเดอร์ซ้ำที่ไม่จำเป็นทุกครั้งสร้างภาระเพิ่มเติมบนการเชื่อมต่อบริดจ์ระหว่าง JavaScript และเธรดเนทีฟ ดังนั้นการใช้ useMemo อย่างถูกต้องส่งผลโดยตรงต่อความลื่นไหลของแอนิเมชันและความเร็วในการตอบสนองของอินเทอร์เฟซ
ประเด็นสำคัญ
useMemo — คือฮุกจากไลบรารีมาตรฐานของ React ที่พร้อมใช้งานใน React Native โดยไม่ต้องมีแพ็กเกจเพิ่มเติม มันจดจำค่าที่ส่งคืนโดยฟังก์ชันที่ส่งผ่านและนำค่านี้กลับมาใช้ใหม่ในการเรนเดอร์ครั้งต่อไปจนกว่าดีเพนเดนซีที่ระบุในอาร์เรย์จะเปลี่ยนแปลง
ชื่อของฮุกมาจาก memoization — เทคนิคการปรับแต่งซึ่งผลลัพธ์ของฟังก์ชันที่ใช้ทรัพยากรสูงถูกเก็บไว้ในแคช ในการเรียกครั้งต่อไปด้วยอาร์กิวเมนต์เดียวกัน ค่าที่เก็บไว้ในแคชจะถูกส่งคืนแทนการคำนวณใหม่
ในบริบทของ React Native การจดจำค่ามีความสำคัญเป็นพิเศษเนื่องจากคุณลักษณะทางสถาปัตยกรรม การเรนเดอร์ซ้ำของคอมโพเนนต์ทุกครั้งส่งข้อมูลผ่าน บริดจ์ JavaScript — เนทีฟ ซึ่งใช้เวลา หากคอมโพเนนต์มีการคำนวณที่ใช้ทรัพยากรสูง (การกรองรายการ การเรียงลำดับ การจัดรูปแบบ) การเรนเดอร์ซ้ำทุกครั้งจะทำซ้ำการคำนวณเหล่านั้น บล็อกเธรด JS และทำให้เฟรมดรอป
ตาม React Native Performance Docs, 2024 คอขวดมักไม่ใช่การเรนเดอร์คอมโพเนนต์เนทีฟ แต่เป็นการดำเนินการโลจิก JavaScript ระหว่างการเรนเดอร์ useMemo แก้ไขปัญหานี้โดยเฉพาะ
useMemo รับฟังก์ชันและอาร์เรย์ดีเพนเดนซี ส่งคืนค่าที่จดจำไว้ React เก็บค่าก่อนหน้าและอาร์เรย์ดีเพนเดนซี ในการเรนเดอร์แต่ละครั้ง React เปรียบเทียบดีเพนเดนซีปัจจุบันกับค่าก่อนหน้าโดยใช้ Object.is หากดีเพนเดนซีอย่างน้อยหนึ่งตัวเปลี่ยนแปลง ฟังก์ชันจะถูกดำเนินการอีกครั้งและผลลัพธ์จะถูกเก็บไว้
ลายเซ็นของ useMemo เหมือนกันบนเว็บและใน React Native อาร์กิวเมนต์แรกคือฟังก์ชันที่ไม่มีพารามิเตอร์ซึ่งส่งคืนค่าที่จะจดจำ อาร์กิวเมนต์ที่สองคืออาร์เรย์ดีเพนเดนซี ซึ่งเมื่อเปลี่ยนแปลงจะทำให้ค่าถูกคำนวณใหม่
import React, { useMemo } from 'react';
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
ในตัวอย่างนี้ sortedList จะถูกคำนวณใหม่เมื่อการอ้างอิงไปยังอาร์เรย์ items เปลี่ยนแปลงเท่านั้น หากเนื้อหาอาร์เรย์เปลี่ยนแต่การอ้างอิงยังคงเดิม (การกลายพันธุ์) useMemo จะไม่ตรวจพบการเปลี่ยนแปลง — นี่เป็นข้อจำกัดที่สำคัญ
React ใช้อัลกอริทึม Object.is เพื่อเปรียบเทียบดีเพนเดนซี ซึ่งทำงานคล้ายกับการเท่ากันอย่างเข้มงวด === แต่จัดการ NaN อย่างถูกต้อง (+0 และ -0 ถือว่าแตกต่าง) ซึ่งหมายความว่าสำหรับชนิดดั้งเดิม (สตริง ตัวเลข บูลีน) การเปรียบเทียบทำงานตามค่า ในขณะที่สำหรับออบเจกต์และอาร์เรย์ — ตามการอ้างอิง
// Object.is({'a': 1}, {'a': 1}) -> false (different refs)
// Object.is(42, 42) -> true (primitive by value)
// Object.is(NaN, NaN) -> true (correct handling of NaN)
การทำความเข้าใจกลไกนี้มีความสำคัญ: หากคุณส่ง ออบเจกต์ใหม่ ในดีเพนเดนซีทุกครั้งที่เรนเดอร์ การจดจำค่าจะไร้ประโยชน์เพราะดีเพนเดนซีจะถูกพิจารณาว่าเปลี่ยนแปลงทุกครั้ง
useMemo มีความสมเหตุสมผลในสามสถานการณ์: การคำนวณที่ใช้ทรัพยากรสูง การส่ง props ที่เสถียรให้กับคอมโพเนนต์ลูก และการรักษาเอกลักษณ์การอ้างอิงของออบเจกต์ ใน React Native ทั้งสามสถานการณ์เกิดขึ้นเป็นประจำเนื่องจากสถาปัตยกรรมของแอปพลิเคชันมือถือ
หากคอมโพเนนต์ประมวลผล อาร์เรย์ข้อมูลขนาดใหญ่ (การกรอง การเรียงลำดับ การจัดกลุ่ม) หรือดำเนินการทางคณิตศาสตร์ที่ใช้ทรัพยากรมาก useMemo ป้องกันการทำซ้ำการคำนวณเหล่านี้ในการเรนเดอร์ซ้ำทุกครั้ง ในแอปพลิเคชันมือถือ ตัวอย่างทั่วไปคือการจัดรูปแบบข้อมูลจาก API ก่อนแสดงในรายการ
เมื่อคอมโพเนนต์ลูกถูกห่อหุ้มใน React.memo มันจะเรนเดอร์ซ้ำเมื่อ props ของมันเปลี่ยนแปลงเท่านั้น หากออบเจกต์ prop ถูกสร้างขึ้นภายในคอมโพเนนต์แม่โดยไม่มี useMemo การอ้างอิงใหม่จะถูกสร้างขึ้นในการเรนเดอร์แม่ทุกครั้ง และคอมโพเนนต์ลูกจะเรนเดอร์ซ้ำโดยไม่จำเป็น ทำให้ประโยชน์ของ React.memo เป็นโมฆะ
ใน React Native สไตล์มักขึ้นอยู่กับ props — ตัวอย่างเช่น ความกว้างขององค์ประกอบคำนวณตาม ขนาดหน้าจอ useMemo อนุญาตให้คำนวณสไตล์ไดนามิกดังกล่าวเมื่อพารามิเตอร์อินพุตเปลี่ยนแปลงเท่านั้น
useMemo ไม่ใช่เครื่องมือปรับแต่งสากล ในหลายสถานการณ์ มันไม่ได้ให้ประโยชน์และอาจทำให้ประสิทธิภาพแย่ลงเนื่องจากค่าใช้จ่ายในการจัดเก็บและเปรียบเทียบดีเพนเดนซี ใน React Native ที่ทุกไบต์ของหน่วยความจำมีความสำคัญ การใช้ useMemo อย่างไม่เลือกโดยไม่วัดผลเป็นสิ่งที่ไม่ควรทำ
ฮุกไม่มีประโยชน์ในสามกรณีหลัก ประการแรก หากการคำนวณ เล็กน้อย (การบวกง่าย การต่อสตริง) ต้นทุนของ useMemo เกินต้นทุนของการคำนวณเอง ประการที่สอง หากดีเพนเดนซี เปลี่ยนแปลงทุกการเรนเดอร์ — ฟังก์ชันจะยังคงถูกดำเนินการ และการจดจำค่าเพียงเพิ่มงานพิเศษ ประการที่สาม หากคอมโพเนนต์เรนเดอร์ไม่บ่อย ค่าใช้จ่ายในการจัดเก็บแคชก็ไม่สมเหตุสมผล
ตาม Kent C. Dodds, 2023 ก่อนเพิ่ม useMemo คุณควรวัดปัญหาด้วย React DevTools Profiler หรือ Performance Monitor ในตัวของ React Native หากการเรนเดอร์ซ้ำของคอมโพเนนต์ใช้เวลาน้อยกว่า 1 มิลลิวินาที — ไม่จำเป็นต้องจดจำค่า
useMemo และ useCallback — ฮุกการจดจำค่าสองตัวของ React ที่มักสับสน ความแตกต่างอยู่ที่สิ่งที่พวกเขาเก็บรักษา: useMemo ส่งคืนผลลัพธ์ของการดำเนินการฟังก์ชัน (ค่าใดๆ) ในขณะที่ useCallback ส่งคืนฟังก์ชันเอง ทั้งสองรับอาร์เรย์ดีเพนเดนซี แต่ไวยากรณ์แตกต่างกัน
| คุณลักษณะ | useMemo | useCallback |
|---|---|---|
| ส่งคืน | ผลลัพธ์ของการดำเนินการฟังก์ชัน | การอ้างอิงถึงฟังก์ชัน |
| อาร์กิวเมนต์แรก | ฟังก์ชันที่ส่งคืนค่า | ฟังก์ชันที่จะจดจำ |
| การใช้งานทั่วไป | การแคชการคำนวณ | Callback ที่เสถียรสำหรับคอมโพเนนต์ลูก |
| ความเท่าเทียม | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
ในทางปฏิบัติ useCallback คือ น้ำตาลเชิงไวยากรณ์ เหนือ useMemo สำหรับกรณีที่คุณต้องการจดจำฟังก์ชัน ใน React Native ฮุกทั้งสองถูกใช้บ่อยเท่าๆ กัน: useMemo — สำหรับข้อมูล (ข้อความที่จัดรูปแบบแล้ว สไตล์ที่คำนวณแล้ว) useCallback — สำหรับตัวจัดการเหตุการณ์ (onPress, onChangeText)
มาดูสองสถานการณ์จริงของการใช้ useMemo ในแอปพลิเคชัน React Native อย่างแรกคือการกรองรายชื่อผู้ติดต่อตามคำค้นหา อย่างที่สองคือการคำนวณสไตล์ไดนามิกตามขนาดหน้าจอ
ในตัวอย่างนี้ useMemo ป้องกันการกรองซ้ำของ รายชื่อผู้ติดต่อจำนวนมาก ในการเรนเดอร์ทุกครั้งหากทั้งคำค้นหาและข้อมูลต้นทางไม่เปลี่ยนแปลง
const ContactList = ({ contacts, searchQuery }) => {
const filteredContacts = useMemo(() => {
if (!searchQuery.trim()) return contacts;
const query = searchQuery.toLowerCase();
return contacts.filter((c) =>
c.name.toLowerCase().includes(query) ||
c.phone.includes(query)
);
}, [contacts, searchQuery]);
return (
<FlatList
data={filteredContacts}
renderItem={renderContact}
keyExtractor={(item) => item.id}
/>
);
};
หากไม่มี useMemo การกรองจะดำเนินการใน ทุกการเรนเดอร์ รวมถึงการเรนเดอร์ที่เกิดจากแอนิเมชันหรือการเปลี่ยนแปลงสถานะในส่วนอื่นของคอมโพเนนต์ ด้วย useMemo การกรองจะทำงานเมื่อ contacts หรือ searchQuery เปลี่ยนแปลงเท่านั้น
ใน React Native สไตล์มักขึ้นอยู่กับ ขนาดหน้าจอ หรือ props ของคอมโพเนนต์ useMemo อนุญาตให้คำนวณออบเจกต์สไตล์ดังกล่าวครั้งเดียวและนำกลับมาใช้ใหม่จนกว่าดีเพนเดนซีจะเปลี่ยนแปลง
const Card = ({ title, isSelected }) => {
const cardStyle = useMemo(() => ({
backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
borderWidth: isSelected ? 2 : 1,
padding: 16,
}), [isSelected]);
return (
<View style={cardStyle}>
<Text>{title}</Text>
</View>
);
};
วิธีการนี้รับประกันว่าออบเจกต์ cardStyle จะคงที่ (การอ้างอิงเดียวกัน) จนกว่า isSelected จะเปลี่ยนแปลง หาก Card ถูกห่อหุ้มใน React.memo รูปแบบนี้จะป้องกันการเรนเดอร์ซ้ำที่ไม่จำเป็นขององค์ประกอบลูก
คำถามที่พบบ่อย
useMemo ส่งคืน ค่าที่จดจำไว้ — ผลลัพธ์ของการดำเนินการฟังก์ชันที่ส่งเป็นอาร์กิวเมนต์แรก ในการเรนเดอร์ครั้งต่อไป React จะส่งคืนค่าที่แคชไว้หากดีเพนเดนซีไม่เปลี่ยนแปลง
useMemo ส่งคืน ผลลัพธ์ ของการดำเนินการฟังก์ชัน (ค่าใดๆ: ตัวเลข ออบเจกต์ JSX) ในขณะที่ useCallback ส่งคืน ฟังก์ชันเอง โปรดทราบว่า useCallback(fn, deps) เทียบเท่ากับ useMemo(() => fn, deps)
useMemo ไร้ประโยชน์สำหรับ การคำนวณเล็กน้อย (การบวก การต่อสตริง) การเรนเดอร์ซ้ำที่หายาก ดีเพนเดนซีที่เปลี่ยนแปลงทุกการเรนเดอร์ และชนิดดั้งเดิม วัดประสิทธิภาพก่อนเพิ่ม useMemo เสมอ
ได้ หากใช้โดยไม่จำเป็น React เก็บค่าก่อนหน้า ในหน่วยความจำ และเปรียบเทียบดีเพนเดนซีทุกการเรนเดอร์ สำหรับการคำนวณธรรมดา ค่าใช้จ่ายของการดำเนินการเหล่านี้เกินประโยชน์ของการจดจำค่า
ใช้ React DevTools Profiler เพื่อวัดเวลาเรนเดอร์ของคอมโพเนนต์ ใน React Native Performance Monitor ก็สามารถเข้าถึงได้ผ่านเมนูนักพัฒนา หากการเรนเดอร์ซ้ำใช้เวลาน้อยกว่า 1 มิลลิวินาที — ไม่จำเป็นต้องใช้ useMemo
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ