React Native useCallback依赖初始状态致元数据统计异常求助
React Native音频元数据追踪问题:length_played始终为0,time_listened数值不准确
我在实现React Native应用的音频收听元数据追踪功能时遇到问题:length_played始终为0,time_listened数值不准确。推测原因是onSpeakDone回调执行时,无法获取spokenText和speakStartTime的更新后状态值。我是React Native新手,求可行的解决思路。
日志输出
!!! {"length": 460, "length_played": 0, "name": "San Diego", "termination_kind": "done", "time_listened": 1718124515} !!!
相关代码
export default function PlaceCard({ className, isCurrent, latitude, longitude, placeName }: PlaceCardProps) { const { place } = usePlace(latitude, longitude, placeName) const { name, summary } = place const speaking = useAppStore(state => state.speaking) const setSpeaking = useAppStore(state => state.setSpeaking) const setCanSpeak = useAppStore(state => state.setCanSpeak) const [spokenText, setSpokenText] = useState('') const [speakStartTime, setSpeakStartTime] = useState(0) const unspokenText = summary.slice(spokenText.length) const onSpeakBoundary = useCallback(({ charIndex, charLength }: NativeBoundaryEvent) => { const spoken = summary.slice(0, charIndex) + summary.slice(charIndex, charIndex + charLength) setSpokenText(spoken) }, [summary]) const onSpeakDone = useCallback((termination: string) => { setSpeaking(false) const metadata = { length: summary.length, length_played: spokenText.length, name, termination_kind: termination, time_listened: Math.round((Date.now() - speakStartTime) / 1000), } console.log('!!!') console.log(metadata) console.log('!!!') logEvent('place_summary_play', metadata) setSpokenText('') setSpeakStartTime(0) }, [spokenText, speakStartTime, name, summary, setSpeaking, setSpokenText, setSpeakStartTime]) useEffect(() => { if (place.summaryState === 'complete') setCanSpeak(true) const options: Speech.SpeechOptions = { language: 'en-US', onBoundary: onSpeakBoundary, onDone: () => onSpeakDone('done'), onError: () => onSpeakDone('error'), onStart: () => setSpeakStartTime(Date.now()), voice: 'com.apple.ttsbundle.siri_Nicky_en-US_compact', } speaking ? Speech.speak(place.summary, options) : Speech.stop() }, [place.summary, place.summaryState, speaking, setCanSpeak, onSpeakDone, onSpeakBoundary]) if (!place) return null return ( <View>{/* rest of the irrelevant code here */}</View> ) }
解决思路
1. 用useRef替代useState存储实时值
useState的更新是异步的,且回调函数会捕获创建时的闭包状态,导致onSpeakDone拿到的是初始值。改用useRef存储spokenText和speakStartTime,ref的current属性是可变的,能直接获取最新值:
// 替换原有的useState const spokenTextRef = useRef('') const speakStartTimeRef = useRef(0) // 修改onSpeakBoundary const onSpeakBoundary = useCallback(({ charIndex, charLength }: NativeBoundaryEvent) => { const spoken = summary.slice(0, charIndex) + summary.slice(charIndex, charIndex + charLength) spokenTextRef.current = spoken setSpokenText(spoken) // 保留setSpokenText用于UI更新 }, [summary]) // 修改onSpeakDone const onSpeakDone = useCallback((termination: string) => { setSpeaking(false) const metadata = { length: summary.length, length_played: spokenTextRef.current.length, name, termination_kind: termination, time_listened: Math.round((Date.now() - speakStartTimeRef.current) / 1000), } console.log('!!!') console.log(metadata) console.log('!!!') logEvent('place_summary_play', metadata) spokenTextRef.current = '' speakStartTimeRef.current = 0 setSpokenText('') setSpeakStartTime(0) }, [name, summary, setSpeaking, setSpokenText, setSpeakStartTime]) // 修改useEffect中的onStart const options: Speech.SpeechOptions = { // ...其他配置 onStart: () => { speakStartTimeRef.current = Date.now() setSpeakStartTime(Date.now()) }, }
2. 验证onBoundary回调是否触发
length_played为0可能是onSpeakBoundary根本没执行,在回调里加日志确认:
const onSpeakBoundary = useCallback(({ charIndex, charLength }: NativeBoundaryEvent) => { console.log('Boundary事件触发:', charIndex, charLength) // 新增日志 const spoken = summary.slice(0, charIndex) + summary.slice(charIndex, charIndex + charLength) spokenTextRef.current = spoken setSpokenText(spoken) }, [summary])
如果没触发,检查使用的语音库是否支持onBoundary事件,或平台(iOS/Android)是否有额外配置要求。
3. 检查useEffect依赖项是否准确
当前useEffect依赖onSpeakDone和onSpeakBoundary,确保这两个useCallback的依赖项完整,避免因依赖变化导致语音实例重复创建,影响事件触发逻辑。
内容的提问来源于stack exchange,提问作者Moon
相关产品推荐
相关产品推荐

