useState变量在Photo Sphere Viewer的handleClick中不生效求助
问题描述
点击按钮可将AddPin状态从false改为true,但点击ReactPhotoSphereViewer组件时,handlePanoClick函数打印的AddPin仍为false,状态变更未生效。尝试过使用var、let等普通变量,但因需要根据状态变更更新组件,必须使用useState。
相关代码:
import React, { useState } from 'react'; import { Grid, Typography, Button, TextField } from "@material-ui/core"; import { ReactPhotoSphereViewer } from 'react-photo-sphere-viewer'; import PANOVIEW from './assets/img/SAG.png'; function App() { const [PinText,setPinText] = useState(''); const [AddPin,setAddPin] = useState(false); const handlePinTextChange = (e) => { setPinText(e.target.value) } const handleNewPinButton = () => { setAddPin(true) } const handlePanoClick = () => { console.log(AddPin) } return ( <React.Fragment> <Grid container> <Grid item xs={2} style={{ padding: "10px" }}> <Grid container> <Grid item xs={12} style={{ padding: "10px" }}> <Typography style={{ fontWeight: "bold" }}>AÑADIR:</Typography> </Grid> <Grid item xs={12} style={{ padding: "10px" }}> <Typography> Añade un pin con un texto. </Typography> </Grid> <Grid item xs={12} style={{ padding: "10px" }}> <TextField id="outlined-basic" label="Texto Pin" variant="outlined" value={PinText} onChange={(e) => handlePinTextChange(e)} /> </Grid> <Grid item xs={12} style={{ padding: "10px" }}> <Button variant="contained" onClick={handleNewPinButton}>Añadir Pin</Button> </Grid> {AddPin ? ( <>PinText</> ):( <>falso</> )} </Grid> </Grid> <Grid item xs={10}> <ReactPhotoSphereViewer src={PANOVIEW} height='100vh' width="100%" onClick={handlePanoClick} ></ReactPhotoSphereViewer> </Grid> </Grid> </React.Fragment> ); } export default App;
问题原因
核心是闭包陷阱:handlePanoClick在组件首次渲染时创建,捕获了当时的AddPin初始值false。更新AddPin为true后,组件重新渲染生成了新的handlePanoClick,但ReactPhotoSphereViewer可能没有重新绑定这个新回调,依然用旧版本,所以打印的还是旧值。部分第三方组件会缓存事件回调,不会自动响应外部props变化,也会导致这个问题。
解决方案
方法1:用useRef保存最新状态
useRef的.current属性会实时更新,不受闭包限制:
import React, { useState, useRef, useEffect } from 'react'; // 其他导入不变 function App() { const [PinText, setPinText] = useState(''); const [AddPin, setAddPin] = useState(false); const addPinRef = useRef(false); // 同步状态到ref useEffect(() => { addPinRef.current = AddPin; }, [AddPin]); // 其他函数不变 const handlePanoClick = () => { console.log(addPinRef.current); // 拿到最新的AddPin值 } // return部分不变 }
方法2:用useCallback包裹回调
给handlePanoClick加上useCallback,把AddPin作为依赖,确保状态更新时回调也更新:
import React, { useState, useCallback } from 'react'; // 其他导入不变 function App() { const [PinText, setPinText] = useState(''); const [AddPin, setAddPin] = useState(false); // 其他函数不变 const handlePanoClick = useCallback(() => { console.log(AddPin); }, [AddPin]); // 依赖AddPin,状态变化时重新生成回调 // return部分不变 }
方法3:强制组件重新渲染
给ReactPhotoSphereViewer加key属性,AddPin变化时触发组件重新初始化,绑定新回调:
<ReactPhotoSphereViewer src={PANOVIEW} height='100vh' width="100%" onClick={handlePanoClick} key={AddPin} // AddPin变化时,组件重新渲染 ></ReactPhotoSphereViewer>
注意事项
- 优先用方法1或2,方法3会导致组件重新初始化,有性能开销,仅在其他方法无效时使用。
- 可查看
react-photo-sphere-viewer官方文档,确认组件是否支持事件回调的动态更新,部分第三方组件有特定的监听更新方式。
内容的提问来源于stack exchange,提问作者alexolguin
相关产品推荐
相关产品推荐

