React useEffect更新状态但未触发重渲染问题排查
React中使用MediaDevices枚举设备后页面未正确渲染的问题修复
问题描述
尝试通过MediaDevices Web API的enumerateDevices方法加载并列出输入设备,使用React的useEffect钩子将设备信息存入state。React开发者工具显示inputs数组已有字符串列表,但页面未因状态更新正确渲染设备项。
原代码:
const App = () => { const [inputs, setInputs] = useState([]); useEffect(() => { let inputDevices = []; navigator.mediaDevices.enumerateDevices().then((devices) => { devices.forEach((device) => inputDevices.push(device.label)); }); setInputs([inputDevices]); }, []); return ( <div> <ol> {inputs.map((device, index) => { return ( <li key={index}> id:{index} - Device: {device}{" "} </li> ); })} </ol> </div> ); };
输出截图:

编辑说明:setInputs([inputDevices])是失误,感谢提醒我Promise的异步特性:)
问题原因及修复方案
1. 异步执行顺序错误
enumerateDevices是异步Promise,原代码在then回调外部就调用了setInputs,此时inputDevices还未被填充设备数据。等到then回调执行时,state已经完成初始化,后续对inputDevices的修改无法触发React状态更新。
2. 数据结构错误
setInputs([inputDevices])将设备数组包裹成了二维数组,导致map遍历的是外层数组,每个device元素是整个设备列表数组,最终渲染出数组的字符串形式。
修正后的代码
const App = () => { const [inputs, setInputs] = useState([]); useEffect(() => { // 等待Promise完成后再处理数据并更新state navigator.mediaDevices.enumerateDevices().then((devices) => { const inputDevices = devices.map(device => device.label); // 直接传入一维设备数组 setInputs(inputDevices); }); }, []); return ( <div> <ol> {inputs.map((device, index) => ( <li key={index}> id:{index} - Device: {device} </li> ))} </ol> </div> ); };
补充:设备标签为空的处理
如果设备label显示为空,是因为浏览器的安全限制:必须先获取媒体权限(摄像头/麦克风),才能获取设备的真实名称。可以在枚举设备前先请求权限:
useEffect(() => { navigator.mediaDevices.getUserMedia({ audio: true, video: true }) .then(() => navigator.mediaDevices.enumerateDevices()) .then((devices) => { const inputDevices = devices.map(device => device.label); setInputs(inputDevices); }) .catch(err => console.error('媒体权限请求失败:', err)); }, []);
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

