React Native录音应用标题赋值异常问题求助
问题原因
你当前的逻辑顺序完全错误:停止录音时立刻就把还没输入的空title添加到了录音列表,等你在弹窗输入标题时,这条空title的记录已经被存进列表;创建第二个录音时,又重复了这个错误,甚至可能因为title状态没及时清空,导致复用了上一次的输入值。
解决方法
核心思路:先完成录音停止并保存录音基础数据,再打开弹窗让用户输入标题,只有当用户点击「Add」确认标题后,才把带标题的录音添加到列表。具体修改如下:
- 新增临时状态存储未命名录音
在现有useState声明后添加:
const [tempRecording, setTempRecording] = useState(null);
- 重写
stopRecording函数
去掉直接添加录音到列表的代码,先处理录音停止,把基础数据存到临时状态,再打开弹窗:
async function stopRecording() { try { await recording.stopAndUnloadAsync(); const { sound, status } = await recording.createNewLoadedSoundAsync(); // 存储录音基础数据,等待用户输入标题 setTempRecording({ sound: sound, file: recording.getURI(), id: parseInt(Math.random() * 1000), }); setRecording(undefined); setModalVisible(true); // 打开弹窗让用户输入标题 } catch (err) { console.error("Failed to stop recording", err); } }
- 修改弹窗的Add按钮逻辑
点击Add时,把临时录音数据加上用户输入的标题,再添加到录音列表,同时清空临时数据和标题:
<Button title="Add" onPress={() => { if (tempRecording && title.trim()) { // 确保有临时数据且标题不为空 setRecordings(prev => [...prev, { ...tempRecording, title: title }]); setTempRecording(null); setTitle(""); } setModalVisible(false); }} />
- 可选优化:弹窗关闭时清空标题
修改Modal的onRequestClose,避免下次打开弹窗时复用旧标题:
onRequestClose={() => { setTitle(""); setModalVisible(false); }}
修改后的完整代码
import { StyleSheet, Text, View, Button, FlatList, TextInput, Modal } from "react-native"; import { Audio } from "expo-av"; import { useState } from "react"; import * as Sharing from "expo-sharing"; export default function App() { const [recording, setRecording] = useState(); const [recordings, setRecordings] = useState([]); const [message, setMessage] = useState(""); const [modalVisible, setModalVisible] = useState(false); const [title, setTitle] = useState(""); // 新增临时状态存储未命名的录音数据 const [tempRecording, setTempRecording] = useState(null); async function startRecording() { try { const permission = await Audio.requestPermissionsAsync(); if (permission.status === "granted") { await Audio.setAudioModeAsync({ allowsRecordingIOS: true, playsInSilentModeIOS: true, }); const { recording } = await Audio.Recording.createAsync( Audio.RecordingOptionsPresets.HIGH_QUALITY ); setRecording(recording); } else { setMessage("Please grant permission to app to access microphone"); } } catch (err) { console.error("Failed to start recording", err); } } async function stopRecording() { try { await recording.stopAndUnloadAsync(); const { sound, status } = await recording.createNewLoadedSoundAsync(); // 存储临时录音数据,等待用户输入标题 setTempRecording({ sound: sound, file: recording.getURI(), id: parseInt(Math.random() * 1000), }); setRecording(undefined); setModalVisible(true); } catch (err) { console.error("Failed to stop recording", err); } } function deleteItem(id) { const filteredData = recordings.filter((item) => item.id !== id); setRecordings(filteredData); } function recordingView(item) { return ( <View style={styles.row}> <Text style={styles.fill}>{item.item.title}</Text> <Button style={styles.button} onPress={() => item.item.sound.replayAsync()} title="Play"></Button> <Button style={styles.button} onPress={() => Sharing.shareAsync(item.item.file)} title="Share"></Button> <Button style={styles.button} onPress={() => deleteItem(item.item.id)} title="Delete"></Button> </View> ); } return ( <> <View style={styles.container}> <Text>{message}</Text> <FlatList data={recordings} keyExtractor={(item) => item.id.toString()} renderItem={recordingView} /> <Button title={recording ? "Stop Recording" : "Start Recording"} onPress={recording ? stopRecording : startRecording} /> <Modal animationType="slide" transparent={true} visible={modalVisible} onRequestClose={() => { setTitle(""); setModalVisible(false); }} > <View style={styles.modalBackdrop}> <View style={styles.generalContainer}> <View style={styles.inputContainer}> <TextInput style={styles.input} placeholder="Recording name" value={title} onChangeText={(text) => setTitle(text)} /> <View style={styles.buttonContainer}> <Button title="Add" onPress={() => { if (tempRecording && title.trim()) { setRecordings(prev => [...prev, { ...tempRecording, title }]); setTempRecording(null); setTitle(""); } setModalVisible(false); }} /> </View> </View> </View> </View> </Modal> </View> </> ); } const styles = StyleSheet.create({ container: { flex: 1, margin: 10, }, row: { flexDirection: "row", alignItems: "center", justifyContent: "center", }, fill: { flex: 1, margin: 16, }, button: { margin: 16, }, input: { textAlignVertical: "top", fontSize: 17, width: "100%", height: 40, marginTop: 10, marginBottom: 10, borderColor: "#ffffff", borderWidth: 2, borderRadius: 15, padding: 10, color: "#ffffff", }, generalContainer: { margin: 30, paddingTop: 10, paddingBottom: 10, backgroundColor: "gray", borderRadius: 15, elevation: 4, }, inputContainer: { padding: 20, alignItems: "center", }, // 新增弹窗背景遮罩,提升体验 modalBackdrop: { flex: 1, justifyContent: "center", backgroundColor: "rgba(0,0,0,0.5)", }, });
额外优化细节:
- FlatList的
keyExtractor改用录音id,避免索引导致的渲染异常 - 新增弹窗背景遮罩,提升交互体验
内容的提问来源于stack exchange,提问作者Onur Serbes
相关产品推荐
相关产品推荐

