React useEffect监听keydown事件页面首次加载失效问题排查
问题分析与修复方案
核心问题
keyPress函数依赖缺失:函数内部用到了ids但未加入useCallback的依赖数组,导致API加载完视频ID后,keyPress仍引用旧的空数组,判断逻辑失效。- URL更新逻辑存在异步问题:调用
setUrl后直接使用url赋值iframe的src,但setUrl是异步操作,此时url还是旧值,无法正确切换视频。 - 边界条件判断错误:原逻辑中
count >= ids.length的判断不符合索引规则(视频ID数组索引从0开始,最后一个元素的索引是ids.length - 1),导致最后一个视频播放时按'e'键不会触发切换。
修复步骤
1. 修正keyPress函数的依赖与判断逻辑
将ids加入useCallback的依赖数组,同时调整边界判断条件,并且增加空数组判断避免无效操作:
const keyPress = useCallback((e) => { if (e.key === magicKey) { // 无视频ID时直接返回 if (ids.length === 0) return; // 播放到最后一个视频时重置count if (count >= ids.length - 1) { setCount(0); } else { setCount(cCount => cCount + 1); } } }, [count, ids, magicKey]);
2. 修复URL更新的useEffect
直接使用计算后的新URL赋值,避免异步状态更新的问题,同时增加边界判断:
useEffect(() => { // 无视频ID或count越界时不执行 if (ids.length === 0 || count >= ids.length) return; const newUrl = baseUrl(ids[count]); setUrl(newUrl); const videoFrame = document.getElementById('screen'); if (videoFrame) { videoFrame.src = newUrl; } }, [ids, count]);
3. 优化初始状态
将url的初始值设为空字符串,避免加载无效的YouTube嵌入链接:
const [url, setUrl] = useState('');
完整修复后的代码
import React, { useEffect, useState, useCallback } from "react"; import { shuffle } from "../utils/shuffle"; import './player.css'; const baseUrl = (id) => `https://www.youtube.com/embed/${id}?autoplay=1`; const magicKey = 'e'; function Player({ searchTerm }) { const [ids, setIds] = useState([]); // youtube video ids const [url, setUrl] = useState(''); const [count, setCount] = useState(0); // Play next video when pressing 'e' const keyPress = useCallback((e) => { if (e.key === magicKey) { if (ids.length === 0) return; if (count >= ids.length - 1) { setCount(0); } else { setCount(cCount => cCount + 1); } } }, [count, ids, magicKey]); useEffect(() => { document.addEventListener("keydown", keyPress); return () => document.removeEventListener("keydown", keyPress); }, [keyPress]); // fetch new ids from backend based on new searchTerm useEffect(() => { console.log('api call for new ids: ', searchTerm); fetch(`http://localhost:4000/videos?term=${searchTerm}`) .then((response) => response.json()) .then((res) => { setIds(shuffle(res.videoIds)); setCount(0); }); }, [searchTerm]); // when the id's change, set a new url for the iframe to play useEffect(() => { if (ids.length === 0 || count >= ids.length) return; const newUrl = baseUrl(ids[count]); setUrl(newUrl); const videoFrame = document.getElementById('screen'); if (videoFrame) { videoFrame.src = newUrl; } }, [ids, count]); return ( <section id="player"> <iframe id="screen" title="youtube-player" src={url} allow='autoplay'></iframe> </section> ); } export default Player;
内容的提问来源于stack exchange,提问作者UncleJerry23
相关产品推荐
相关产品推荐

