You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native录音应用标题赋值异常问题求助

问题原因

你当前的逻辑顺序完全错误:停止录音时立刻就把还没输入的空title添加到了录音列表,等你在弹窗输入标题时,这条空title的记录已经被存进列表;创建第二个录音时,又重复了这个错误,甚至可能因为title状态没及时清空,导致复用了上一次的输入值。

解决方法

核心思路:先完成录音停止并保存录音基础数据,再打开弹窗让用户输入标题,只有当用户点击「Add」确认标题后,才把带标题的录音添加到列表。具体修改如下:

  1. 新增临时状态存储未命名录音
    在现有useState声明后添加:
const [tempRecording, setTempRecording] = useState(null);
  1. 重写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);
  }
}
  1. 修改弹窗的Add按钮逻辑
    点击Add时,把临时录音数据加上用户输入的标题,再添加到录音列表,同时清空临时数据和标题:
<Button 
  title="Add" 
  onPress={() => {
    if (tempRecording && title.trim()) { // 确保有临时数据且标题不为空
      setRecordings(prev => [...prev, { ...tempRecording, title: title }]);
      setTempRecording(null);
      setTitle("");
    }
    setModalVisible(false);
  }} 
/>
  1. 可选优化:弹窗关闭时清空标题
    修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:10:36