Một màn hình nội dung ngắn đến vừa cần nút đưa người dùng về đầu. Bạn không cần tạo lại danh sách hoặc thay key của ScrollView chỉ để đổi vị trí cuộn. Gắn ref vào đúng component rồi gọi method cuộn từ hành động của người dùng.
Bài đối chiếu React Native 0.88 và tài liệu React useRef hiện hành ngày 04/11/2026. Hàm tạo dữ liệu thuần JavaScript đã chạy bằng Node 24.18.1. Demo JSX được rà thủ công, chưa build React Native, chạy Hermes hoặc quan sát cuộn trên thiết bị.
Dùng object options để tránh nhầm arguments
API scrollTo nhận object có x, y và animated. Ví dụ dùng y bằng 0 để về đầu vùng cuộn dọc, x bằng 0 và tắt animation để thao tác minh họa không phụ thuộc chuyển động. Tài liệu khuyến cáo dùng object thay chữ ký positional cũ có thứ tự dễ gây nhầm.
Ref giữ tham chiếu dùng cho thao tác imperative này. Nó không phải state cần render số liệu ra giao diện. Trong demo, nút nằm bên ngoài ScrollView để vẫn có thể bấm khi nội dung đã cuộn xuống dưới.
Một màn hình có thể tích hợp vào project thử
Đặt vào App.jsx của project React Native đang chạy:
import React, {useRef} from 'react';
import {Pressable, ScrollView, StyleSheet, Text, View} from 'react-native';
function makeRows() {
return Array.from({length: 30}, (_, index) => ({
id: String(index + 1),
title: `Dòng minh họa ${index + 1}`,
}));
}
const rows = makeRows();
export default function App() {
const scrollRef = useRef(null);
function goToTop() {
scrollRef.current?.scrollTo({x: 0, y: 0, animated: false});
}
return (
<View style={styles.screen}>
<Pressable
style={styles.button}
accessibilityRole="button"
onPress={goToTop}>
<Text style={styles.buttonText}>Về đầu danh sách</Text>
</Pressable>
<ScrollView
ref={scrollRef}
style={styles.list}
contentContainerStyle={styles.content}>
{rows.map(row => (
<Text key={row.id} style={styles.row}>{row.title}</Text>
))}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
screen: {flex: 1, paddingTop: 24},
button: {padding: 16, backgroundColor: '#dbeafe'},
buttonText: {fontSize: 16, color: '#172554'},
list: {flex: 1},
content: {padding: 16},
row: {fontSize: 18, color: '#0f172a', paddingVertical: 16},
});
Hàm makeRows tạo 30 bản ghi với ID ổn định trong demo. Kiểm tra Node chỉ xác nhận dữ liệu đó, không tạo native ScrollView hoặc chứng minh animation hoạt động. Hai style flex giúp vùng cuộn nhận không gian còn lại trong parent đã được app cấp chiều cao.
Kiểm tra trên màn hình thật
Cuộn xuống giữa nội dung rồi bấm nút. Kỳ vọng cần xác nhận là offset về đầu và dữ liệu không bị tạo lại chỉ vì thao tác đó. Thử bấm khi đang ở đầu: hành động vẫn nên hợp lý, không cần tìm một item để scrollToIndex.
Nếu nội dung chưa cao hơn viewport, việc không thấy chuyển vị trí không tự chứng minh method thất bại. Trước hết kiểm tra màn hình có đủ nội dung để cuộn, ref có gắn đúng ScrollView và callback có được gọi hay không.
Ref có thể chưa có đối tượng hiện tại
Optional chaining bảo vệ trường hợp current đang null. Nó không biến thao tác bị bỏ qua thành một lần cuộn đã thành công. Gọi trong callback sau mount giúp mục đích rõ hơn việc gọi method ngay trong render.
Nếu bọc ScrollView bằng component riêng, cần thiết kế cách truyền ref hoặc API điều khiển phù hợp. Demo gắn trực tiếp nên không kiểm chứng mọi wrapper hay thư viện navigation. Cũng không tự cuộn lên mỗi lần data đổi, vì việc đó có thể làm gián đoạn người dùng đang đọc.
Chọn đúng loại danh sách
ScrollView tạo các child trong ví dụ cùng lúc. Ba mươi dòng nhẹ chỉ là fixture; không dùng bài này làm bằng chứng nó phù hợp với hàng nghìn row phức tạp. Với dữ liệu dài, xem loại list và method tương ứng theo yêu cầu đo đạc thực tế.
Tách thao tác cuộn khỏi dữ liệu và key giúp luồng dễ kiểm tra. Xác nhận ref, constraint và vị trí mục tiêu trên thiết bị trước, rồi mới bổ sung animation hoặc chính sách tự động cuộn cho sản phẩm.


