คอมโพเนนต์ ScrollView ใน React Native คือคอนเทนเนอร์ที่ช่วยให้เลื่อนเนื้อหาที่เกินขนาดหน้าจอได้ ต่างจาก FlatList ตรงที่ ScrollView จะเรนเดอร์เนื้อหาทั้งหมดในครั้งเดียว ซึ่งเหมาะสำหรับข้อมูลปริมาณน้อย ฟอร์ม และหน้าจอที่มีความสูงที่ทราบ ตาม React Native Docs, 2024 ScrollView รองรับการเลื่อนแนวตั้งและแนวนอน เหตุการณ์ onScroll การควบคุมตำแหน่งแบบโปรแกรม และท่าทางการซูม คอมโพเนนต์นี้จะเรนเดอร์เป็น UIScrollView ดั้งเดิมบน iOS และ ScrollView บน Android
ประเด็นสำคัญ
ScrollView คือคอมโพเนนต์ React Native ที่ห่อเนื้อหาของมันในคอนเทนเนอร์ที่เลื่อนได้ หากเนื้อหาเกินขอบเขตหน้าจอ ผู้ใช้สามารถเลื่อนในแนวตั้งหรือแนวนอนได้ ScrollView จะเรนเดอร์องค์ประกอบย่อยทั้งหมดในขณะ mount โดยไม่คำนึงว่าจะมองเห็นบนหน้าจอหรือไม่
ต่างจาก FlatList ซึ่งเรนเดอร์เฉพาะ องค์ประกอบที่มองเห็น (lazy rendering) ScrollView จะสร้างวิวพื้นเมืองทั้งหมดทันที ทำให้ ScrollView สะดวกสำหรับฟอร์ม หน้าการตั้งค่า การ์ดสินค้าบนหน้าจอเดียว — ทุกที่ที่จำนวนองค์ประกอบย่อยน้อย (สูงสุด 20-30) หรือไม่ทราบจำนวนที่แน่นอนล่วงหน้า
ตาม React Native Using ScrollView, 2024 ScrollView รองรับการเลื่อน แบบซ้อน (เช่น ScrollView ภายใน ScrollView) เอฟเฟกต์เด้งเมื่อถึงขอบ ตัวบ่งชี้การเลื่อน การจัดตำแหน่งแบบ snap และการจัดการท่าทาง บน iOS ScrollView จะเพิ่มระยะ SafeArea โดยอัตโนมัติผ่าน contentInsetAdjustmentBehavior
ScrollView มีชุดคุณสมบัติมากมายสำหรับควบคุมพฤติกรรมการเลื่อน ลักษณะที่ปรากฏ และการโต้ตอบ มาดูกลุ่มหลักกัน: การควบคุมการเลื่อน การตั้งค่าภาพ เหตุการณ์ และคุณสมบัติเฉพาะแพลตฟอร์ม
horizontal — เปลี่ยนทิศทางการเลื่อนจากแนวตั้ง (ค่าเริ่มต้น) เป็นแนวนอน scrollEnabled — ปิดการเลื่อนเมื่อจำเป็น showsVerticalScrollIndicator และ showsHorizontalScrollIndicator ควบคุมการแสดงตัวบ่งชี้การเลื่อน bounces (iOS) เปิด/ปิดเอฟเฟกต์เด้งเมื่อถึงขอบเนื้อหา
เหตุการณ์หลักคือ onScroll ซึ่งถูกเรียกทุกครั้งที่มีการเลื่อนและส่ง object ที่มี nativeEvent ซึ่งประกอบด้วย contentOffset (ตำแหน่งปัจจุบัน), contentSize (ขนาดเนื้อหา), layoutMeasurement (ขนาดพื้นที่ที่มองเห็น) onMomentumScrollEnd ถูกเรียกหลังจากเลื่อนแบบเฉื่อยเสร็จสิ้น onScrollEndDrag — เมื่อผู้ใช้ยกนิ้วออกจากหน้าจอ เหตุการณ์เหล่านี้ใช้สำหรับการเลื่อนแบบไม่สิ้นสุด เอฟเฟกต์พารัลแลกซ์ และการโหลดแบบขี้เกียจ
ScrollView มีวิธีการสำหรับการควบคุม แบบโปรแกรม ผ่าน ref: scrollTo({x, y, animated}) เลื่อนไปยังพิกัดที่ระบุ, scrollToEnd({animated}) — เลื่อนไปยังจุดสิ้นสุดของเนื้อหา, flashScrollIndicators() — แสดงตัวบ่งชี้การเลื่อนชั่วคราว
const scrollRef = useRef(null);
// เลื่อนขึ้นด้านบน
scrollRef.current.scrollTo({ x: 0, y: 0, animated: true });
// เลื่อนไปที่จุดสิ้นสุด
scrollRef.current.scrollToEnd({ animated: true });
การควบคุม การเลื่อน แบบโปรแกรมมักใช้ในแชท (เลื่อนอัตโนมัติไปยังข้อความล่าสุด), ฟอร์ม (เลื่อนไปยังฟิลด์ที่มีข้อผิดพลาด) และม้าหมุน (การปัดแบบโปรแกรม)
การเลือกระหว่าง ScrollView และ FlatList เป็นหนึ่งในการตัดสินใจทางสถาปัตยกรรมที่สำคัญใน React Native คอมโพเนนต์ทั้งสองให้การเลื่อน แต่ใช้กลยุทธ์การเรนเดอร์ที่แตกต่างกันโดยพื้นฐาน ScrollView เรนเดอร์ทุกอย่างในครั้งเดียว ส่วน FlatList เรนเดอร์เฉพาะองค์ประกอบที่มองเห็น
| คุณลักษณะ | ScrollView | FlatList |
|---|---|---|
| การเรนเดอร์ | องค์ประกอบทั้งหมดในครั้งเดียว | เฉพาะที่มองเห็น + บัฟเฟอร์ |
| ประสิทธิภาพ | ลดลงเมื่อมี 30+ องค์ประกอบ | คงที่ในทุกจำนวน |
| เนื้อหา | คอมโพเนนต์ใดก็ได้ (ฟอร์ม, การ์ด) | องค์ประกอบที่เหมือนกันจากอาร์เรย์ |
| ขนาดข้อมูล | เล็กถึงกลาง (สูงสุด 30 องค์ประกอบ) | ใหญ่และไม่จำกัด |
| การเลื่อนแนวนอน | ใช่ (prop horizontal) | ใช่ (horizontal) |
| ส่วน | ไม่ (เรนเดอร์ตามที่มี) | SectionList สำหรับส่วน |
กฎการเลือก: ScrollView — สำหรับหน้าจอที่ทราบจำนวน องค์ประกอบย่อย และไม่เกิน 20-30 (โปรไฟล์ผู้ใช้, ฟอร์มการตั้งค่า, หน้าสินค้า) FlatList — สำหรับรายการที่มีองค์ประกอบจำนวนมาก (ฟีดข่าว, แคตตาล็อกสินค้า, ข้อความแชท) หากเนื้อหาต่างชนิดกัน (ส่วนหัว, ฟอร์ม, การ์ด, ส่วนท้าย) — ScrollView เหมาะกว่าเพราะ FlatList คาดหวังองค์ประกอบที่เหมือนกัน
ScrollView รองรับการเลื่อนแนวนอนผ่าน prop horizontal ทำให้เหมาะสำหรับม้าหมุน แกลเลอรีรูปภาพ แท็บ และรายการแนวนอน เมื่อรวมกับ prop pagingEnabled (iOS) หรือ snapToInterval ScrollView จะกลายเป็นคอมโพเนนต์สำหรับการนำทางแบบหน้า
คุณสมบัติ horizontal เปลี่ยนทิศทางการเลื่อน pagingEnabled (iOS) เปิดใช้งานการเลื่อนแบบหน้า ซึ่งการเลื่อนจะหยุดที่ขอบเขตขององค์ประกอบเท่านั้น snapToInterval กำหนดขั้นตอนการ snap เป็นพิกเซลสำหรับทั้งสองแพลตฟอร์ม
const ImageCarousel = ({ images }) => (
<ScrollView
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onMomentumScrollEnd={handlePageChange}
>
{images.map((img, index) => (
<Image
key={index}
source={{ uri: img }}
style={{ width: SCREEN_WIDTH, height: 250 }}
/>
))}
</ScrollView>
);
ในม้าหมุนนี้ pagingEnabled จะจัดตำแหน่งการเลื่อนไปยังขอบของแต่ละรูปภาพโดยอัตโนมัติ onMomentumScrollEnd ช่วยให้อัปเดตตัวบ่งชี้หน้าปัจจุบัน ม้าหมุนแบบนี้ทำงานโดยธรรมชาติและรองรับการเลื่อนแบบเฉื่อย
สำหรับขั้นตอน snap ที่ไม่ได้มาตรฐาน ให้ใช้ snapToInterval (ขนาดขั้นตอนเป็นพิกเซล) และ snapToAlignment (start, center, end) ด้วย snapToOffsets คุณสามารถระบุอาร์เรย์ของตำแหน่งเฉพาะสำหรับการ snap ซึ่งมีประโยชน์สำหรับรายการแนวนอนที่มีการ์ดขนาดต่างกัน
มาดูสองสถานการณ์ทั่วไป: หน้าจอพร้อมฟอร์ม (เลื่อนแนวตั้ง) และรายการหมวดหมู่แนวนอน ทั้งสองใช้ ScrollView ด้วย prop ต่างกันเพื่อพฤติกรรมที่ดีที่สุด
หน้าแก้ไขโปรไฟล์ประกอบด้วย องค์ประกอบที่ต่างชนิดกัน: อวาตาร์, ฟิลด์ป้อนข้อมูลหลายฟิลด์, สวิตช์ และปุ่มบันทึก ScrollView ช่วยให้เลื่อนทั้งหน้าเป็นชิ้นเดียวกัน แทนที่จะเลื่อนแต่ละบล็อกแยกกัน
const ProfileScreen = () => (
<ScrollView
contentContainerStyle={styles.container}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
<AvatarPicker />
<TextInput placeholder="Name" />
<TextInput placeholder="Email" />
<TextInput placeholder="About me" multiline />
<SwitchRow label="Notifications" />
<Button title="Save" />
</ScrollView>
);
prop keyboardShouldPersistTaps: 'handled' อนุญาตให้เลื่อนและโต้ตอบกับฟอร์มเมื่อแป้นพิมพ์เปิดอยู่ contentContainerStyle ใช้ระยะห่างกับเนื้อหาทั้งหมด รูปแบบนี้ใช้ในหน้าจอฟอร์มทั้งหมดใน React Native
สำหรับการนำทางผ่าน หมวดหมู่ (แชท, ตัวกรอง, แท็บ) จะใช้ ScrollView แนวนอนพร้อมชิป คอมโพเนนต์นี้พบได้ทั่วไปในแอปพลิเคชันตลาดซื้อขายและโซเชียลมีเดีย
const CategoryChips = ({ categories, selected, onSelect }) => (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.chipContainer}
>
{categories.map((cat) => (
<Chip
key={cat.id}
label={cat.name}
isSelected={selected === cat.id}
onPress={() => onSelect(cat.id)}
/>
))}
</ScrollView>
);
ScrollView แนวนอนที่ปิด ตัวบ่งชี้ เป็นมาตรฐานสำหรับแถบหมวดหมู่ แต่ละชิปเป็นคอมโพเนนต์แยกต่างหากที่มีสถานะการเลือก prop showsHorizontalScrollIndicator: false จะลบแถบเลื่อน ซึ่งสอดคล้องกับความคาดหวังของผู้ใช้แอปมือถือ
คำถามที่พบบ่อย
ScrollView เหมาะสำหรับ ปริมาณน้อย ของข้อมูล (สูงสุด 20-30 องค์ประกอบ) และเนื้อหาต่างชนิดกัน (ฟอร์ม, โปรไฟล์, หน้าจอการตั้งค่า) เลือก FlatList สำหรับรายการ ยาว ที่เหมือนกัน — ฟีดข่าว, แคตตาล็อกสินค้า, ข้อความ
ใช้เหตุการณ์ onScroll กับ onScrollEndDrag หรือ onMomentumScrollEnd ตำแหน่งแนวตั้งปัจจุบันอยู่ใน nativeEvent.contentOffset.y เพื่อประสิทธิภาพ ให้ใช้ ScrollEventThrottle (iOS) เพื่อปรับความถี่การเรียก onScroll
ScrollView เรนเดอร์ องค์ประกอบทั้งหมด ในขณะ mount สร้างวิวพื้นเมืองสำหรับแต่ละรายการ เมื่อมี 50+ องค์ประกอบบนหน้าจอ โหลดหน่วยความจำและ JS thread จะเพิ่มขึ้น ทำให้เฟรมดรอป ในกรณีเช่นนี้ ให้ใช้ FlatList ที่มีการเรนเดอร์แบบขี้เกียจ
ตั้งค่า bounces={false} prop นี้ทำงานเฉพาะบน iOS และปิดเอฟเฟกต์สปริงเมื่อถึงขอบของ ScrollView บน Android ไม่มีเอฟเฟกต์เด้งโดยค่าเริ่มต้น ดังนั้น prop จะถูกไม่สนใจ
ScrollView ไม่รองรับการเลื่อนแบบไม่สิ้นสุดโดยตรง อย่างไรก็ตาม คุณสามารถรวม onScroll เข้ากับการตรวจสอบความใกล้กับขอบล่าง (contentOffset.y + ความสูงหน้าจอ >= contentSize.height) และเพิ่มองค์ประกอบใน state สำหรับปริมาณมาก ให้ใช้ FlatList กับ onEndReached
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม