React.js中Cannot read properties of undefined (reading 'value')报错求助
我正在用React开发一款可控制家电的Web应用前端,需求是通过LightSliderBar中的滑块调节灯光亮度,并在滑块移动时将整数值发送至后端。但现在遇到报错:
Cannot read properties of undefined (reading 'value')
奇怪的是,移动滑块时能在控制台看到handleSliderChange中的console.log(e)输出,却不清楚报错原因。
相关代码
LightDetailOffice.js
import React, { useState, useEffect, useCallback, onClick, useLayoutEffect } from 'react'; import axios from 'axios'; import Cookies from 'universal-cookie'; import { useSelector } from "react-redux"; import { useParams, Link, useHistory } from 'react-router-dom' import LightSliderBar from '../../slider_bar/LightSliderBar'; const LightDetail = () => { const history = useHistory(); const [light, setLight] = useState([]); const {id} = useParams(); const isLoggedIn= useSelector(state => state.user.isLoggedIn); const { entity_id } = useParams(); const getDevices = async(data) => { await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log(result.data) setLight(result.data.attributes.light); }) .catch(err => { console.log(err); }); } useEffect(() => { getDevices(); }, []); return ( <div> <HeaderForDetailPage /> <p className="office_top">Office</p> <div className="container container_light_detail"> {isLoggedIn ? <div className="row mx-auto text-center"> <> <div> <LightSliderBar /> </div> </> </div> : <div> <p> You should login</p> </div> } </div> </div> ); } export default LightDetailOffice;
LightSliderBarOffice.js
import React, { useState, useEffect, useCallback, onClick, useLayoutEffect } from 'react'; import axios from 'axios'; import { useParams, Link, useHistory } from 'react-router-dom' import Cookies from 'universal-cookie'; const LightSliderBar = () => { const cookies = new Cookies(); const { entity_id } = useParams(); const [light, setLight] = useState([]); const [brightness_value, setBrightnessValue] = useState(); let percent = (brightness_value * 100) / 100; let gradient =`linear-gradient(to right,#3C91E6 ` + percent + `%,#fff ` + percent + `%)`; const handleSliderChange = (e) => { console.log(e); lightOn(e.target.value); } const lightOn = async(data) => { console.log("Body sent to server", { entity_id: entity_id, rgb_color: [255, 255, 255], brightness: brightness_value }) await axios.post('xxx.com', { entity_id: entity_id, rgb_color: [255, 255, 255], brightness: brightness_value }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log('Turn on!'); getDevices(); }) .catch(err => { console.log('Turn on Missed!'); }); } const getDevices = async(data) => { await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log(result.data) setLight(result.data.attributes.light); setBrightnessValue(result.data.attributes.light.filter(c => c.entity_id === entity_id).map((item,i) => item.brightness)[0]); }) .catch(err => { console.log(err); }); } useEffect(() => { getDevices(); }, []); return ( <div> <div className="range-container col-8"> <input className="range-input" type="range" name="speed" min="0" max="100" id="slider" value={brightness_value} onChange={(e) => handleSliderChange(e.target.value)} style={{ background: gradient, }}></input> </div> </div> ); }; export default LightSliderBarOffice;
问题原因
看代码里的滑块onChange绑定:
onChange={(e) => handleSliderChange(e.target.value)}
这里直接把滑块的value值传给了handleSliderChange,但handleSliderChange函数里却写了lightOn(e.target.value)——此时函数里的e已经是传入的value(比如数字50),不是事件对象,自然没有target属性,所以会抛出Cannot read properties of undefined (reading 'value')的错误。
你控制台看到的console.log(e)输出,其实是滑块的value值,不是事件对象,这就是问题所在。
修复方案
方案一:直接使用传入的value值
修改handleSliderChange,直接用参数作为亮度值,同时更新状态并传给lightOn:
const handleSliderChange = (value) => { console.log(value); setBrightnessValue(value); // 更新状态 lightOn(value); // 把当前value传给lightOn }
然后修改lightOn,使用传入的value(而不是依赖状态,因为状态更新是异步的,滑块移动时状态还没更新):
const lightOn = async(brightnessValue) => { console.log("Body sent to server", { entity_id: entity_id, rgb_color: [255, 255, 255], brightness: brightnessValue // 用传入的实时值 }) await axios.post('xxx.com', { entity_id: entity_id, rgb_color: [255, 255, 255], brightness: brightnessValue }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log('Turn on!'); getDevices(); }) .catch(err => { console.log('Turn on Missed!'); }); }
方案二:传递事件对象给handleSliderChange
把滑块的onChange改成传递事件对象:
<input className="range-input" type="range" name="speed" min="0" max="100" id="slider" value={brightness_value} onChange={(e) => handleSliderChange(e)} // 传事件对象 style={{ background: gradient, }} ></input>
然后修改handleSliderChange,从事件对象里取value,更新状态后传给lightOn:
const handleSliderChange = (e) => { const value = e.target.value; console.log(value); setBrightnessValue(value); lightOn(value); }
同样,lightOn函数要改成使用传入的brightnessValue,避免依赖异步更新的状态。
额外优化点
- 初始值问题:
brightness_value初始是undefined,计算percent时会得到NaN,导致渐变样式失效,建议初始设为0:
const [brightness_value, setBrightnessValue] = useState(0);
- 数组越界风险:
getDevices里用filter+map取brightness,如果找不到匹配的entity_id,会返回undefined,建议用find优化:
const getDevices = async(data) => { await axios.get('xxx.com', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${cookies.get('accesstoken')}` }, }) .then(result => { console.log(result.data) setLight(result.data.attributes.light); const targetDevice = result.data.attributes.light.find(c => c.entity_id === entity_id); setBrightnessValue(targetDevice ? targetDevice.brightness : 0); // 找不到就设为0 }) .catch(err => { console.log(err); }); }
内容的提问来源于stack exchange,提问作者user20271619

