Bạn đã đăng ký BackHandler cho màn hình nhưng khi mở Modal, callback không còn chạy. Thêm một listener khác thường không giải quyết đúng vấn đề: Modal có đường nhận yêu cầu đóng riêng mà component cần xử lý.
Bài này tạo một hộp thông tin có hai đường đóng: nút Đóng và Back Android. Tài liệu React Native được đọc ngày 25/10/2026. Ví dụ đã được rà thủ công theo API, chưa build hoặc thử trên thiết bị Android/iOS trong lượt biên tập; hãy kiểm tra bằng phiên bản React Native của dự án.
1. Dùng onRequestClose cho yêu cầu từ hệ thống
Tài liệu Modal mô tả onRequestClose cho nút Back Android và lưu ý BackHandler không phát sự kiện khi Modal mở. Tham chiếu BackHandler nhắc lại giới hạn này. Vì vậy, callback của màn hình phía dưới không phải nơi phù hợp để chờ xử lý lần bấm Back đó.
Prop visible là nguồn quyết định hiển thị. Nhận yêu cầu đóng và cập nhật visible là hai việc nối với nhau trong code của bạn. Trong ví dụ, cả nút trong giao diện và callback hệ thống đều đi qua một hàm đặt visible về false.
2. Một màn hình minh họa tự đủ
Đặt component sau vào một màn hình có parent flex: 1. Dự án vẫn cần cấu hình app, navigation và safe area theo cách đang dùng; Modal này không thay thế các phần setup đó.
import React, {useState} from 'react';
import {Button, Modal, StyleSheet, Text, View} from 'react-native';
export default function ModalDemo() {
const [visible, setVisible] = useState(false);
const close = () => setVisible(false);
return (
<View style={styles.screen}>
<Button title="Xem thông tin" onPress={() => setVisible(true)} />
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={close}
supportedOrientations={['portrait', 'landscape']}>
<View style={styles.overlay}>
<View style={styles.card}>
<Text style={styles.title}>Thông tin bản thử</Text>
<Text style={styles.body}>
Bạn có thể đóng bằng nút bên dưới hoặc Back trên Android.
</Text>
<Button title="Đóng" onPress={close} />
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
screen: {flex: 1, justifyContent: 'center', padding: 24},
overlay: {flex: 1, padding: 24, justifyContent: 'center',
alignItems: 'center', backgroundColor: 'rgba(0, 0, 0, 0.45)'},
card: {width: '100%', maxWidth: 420, padding: 20,
borderRadius: 16, backgroundColor: '#ffffff'},
title: {fontSize: 22, fontWeight: '700', marginBottom: 12},
body: {fontSize: 16, marginBottom: 20},
});
Nền mờ là View mà ứng dụng tự vẽ. Prop transparent cho phép nền Modal trong suốt; nó không tự tạo chính sách “chạm bên ngoài để đóng”. Ví dụ không gắn một hành động vào nền, nên người dùng có nút Đóng rõ ràng và đường Back Android.
Hộp không có chiều cao cố định để text có thể xuống dòng. Tuy vậy, nội dung rất dài hoặc cỡ chữ lớn vẫn cần thiết kế cuộn và safe area phù hợp; không suy ra ví dụ ngắn đã đáp ứng mọi kích thước thiết bị.
3. Giữ một nguồn trạng thái
Nếu cha sở hữu visible, hãy truyền callback yêu cầu đóng về cha thay vì tạo thêm một biến local độc lập. Hai biến “đang mở” không được cập nhật cùng nhau có thể khiến modal hiện lại sau một lần render hoặc nút trong modal đóng nhưng cha vẫn nghĩ nó đang mở.
Đặt những chính sách liên quan trong cùng đường xử lý. Ví dụ, với một form có thay đổi chưa lưu, cả nút đóng và Back phải hỏi cùng một quyết định nghiệp vụ. Không để Back bỏ qua bước xác nhận chỉ vì nó đi qua callback khác.
Đoạn minh họa chỉ có thông tin, nên close là thao tác đồng bộ đơn giản. Nếu việc đóng cần chờ lưu dữ liệu, hãy thiết kế trạng thái chờ và lỗi trước khi thêm thao tác bất đồng bộ vào callback; không ghi nhận thành công chỉ vì visible đã về false.
4. Phân biệt các callback giữa nền tảng
OnDismiss trong tài liệu là callback iOS sau khi modal đã bị dismiss. Không dùng nó làm điểm duy nhất cho nghiệp vụ đóng trên cả Android và iOS. Các kiểu trình bày pageSheet hoặc formSheet và thao tác kéo trên iOS cũng có hành vi riêng được tài liệu Modal mô tả.
Ví dụ dùng transparent và fade, không bật swipe dismissal. SupportedOrientations cho phép các hướng được khai báo, nhưng trên iOS vẫn chịu giới hạn của Info.plist ứng dụng. Hãy đọc cấu hình app cùng prop, thay vì xem một mảng trong JSX là quyền xoay màn hình tuyệt đối.
Kiểm tra các đường đóng
Trên Android, mở Modal rồi bấm Back hệ thống. Xác nhận hộp đóng và màn hình bên dưới vẫn ở trạng thái mong đợi. Mở lại, dùng nút Đóng, rồi lặp vài lần để tìm dấu hiệu visible bị lệch nguồn trạng thái.
Trên iOS, thử nút Đóng và các hướng mà app cho phép. Với cả hai nền tảng, tăng cỡ chữ và thử thiết bị hẹp. Nếu hộp có nội dung tương tác, bổ sung kiểm tra thứ tự focus và công nghệ hỗ trợ trong dự án; bài này chưa xác nhận hành vi screen reader thực tế.
Lỗi thường gặp
Nếu BackHandler im lặng khi hộp mở, kiểm tra onRequestClose trước khi đăng ký thêm listener. Nếu callback đã chạy mà hộp vẫn hiện, kiểm tra visible có thật sự được cập nhật ở chủ sở hữu không. Nếu chỉ iOS có callback sau khi đóng, xem lại phạm vi nền tảng của onDismiss.
Nếu muốn chạm nền để đóng, thêm hành vi đó có chủ đích và kiểm tra thao tác bên trong card không bị hiểu nhầm là chạm nền. Không gắn cùng một handler cho mọi vùng chạm mà chưa xác minh luồng sự kiện.
Kết luận
Hãy coi Modal là một component có trạng thái hiển thị và đường yêu cầu đóng rõ ràng. Dùng onRequestClose cho Back Android, giữ nguồn visible thống nhất và kiểm tra từng cách đóng sẽ giúp bạn tránh các bản vá listener không đúng chỗ.




