React setState hook无法设置值:点击触发setClickedMovieId后仍返回默认值
问题根因
有两个核心问题导致当前现象:
- 你把状态更新按钮放在了
<NavLink>组件内部,点击按钮时事件会向上冒泡触发<NavLink>的跳转行为,而React的setState是异步更新的,跳转触发时状态还未完成更新,因此读取到的clickedMovieId始终是旧值0。 - 从逻辑设计上你完全不需要用全局状态存储点击的电影id,每个遍历出来的电影条目本身就持有唯一id,额外用状态拼接路由属于冗余逻辑,反而容易引发问题。你观察到的控制台始终输出0,也是因为状态还没更新完成页面就已经跳转,组件卸载后监听逻辑不会再执行。
修复方案
方案1:精简逻辑(更推荐)
直接删除冗余的clickedMovieId状态和对应的监听useEffect,给每个电影的<NavLink>直接绑定当前条目的id即可,代码修改后如下:
const MovieCards = () => { const { movies } = useContext(MoviesContext); return ( <div > <Grid container spacing={1}> {movies.map((movie) => ( <Grid item xs={6} sm={2} key={movie.id}> {/* 直接用当前遍历到的movie.id拼接路径,不需要额外状态 */} <NavLink to={`movie/${movie.id}`}> <Card className="card"> <CardActionArea> <CardMedia component="img" /> </CardActionArea> <CardActions> <Button size="small" color="primary"> <FavoriteBorderIcon /> </Button> <Button size="small" color="primary"> {movie.rating} </Button> </CardActions> </Card> </NavLink> </Grid> ))} </Grid> </div> ); } export default MovieCards;
方案2:如果确实需要保留clickedMovieId状态做其他逻辑
你需要阻止按钮点击事件冒泡到上层的<NavLink>,同时在状态更新完成后再手动触发跳转,不要用<NavLink>的默认跳转行为:
- 先从react-router-dom引入
useNavigate钩子 - 移除包裹Card的
<NavLink>组件 - 按钮点击事件中先更新状态,再手动跳转
修改后代码示例:
import { useNavigate } from 'react-router-dom'; const MovieCards = () => { const { movies } = useContext(MoviesContext); const navigate = useNavigate(); const [clickedMovieId, setClickedMovieId] = useState<number>(0); useEffect(() => { console.log('Current value of movie id state', clickedMovieId); // 状态更新完成后再跳转 if (clickedMovieId !== 0) { navigate(`movie/${clickedMovieId}`); } }, [clickedMovieId, navigate]); const handleMovieClick = (e, id) => { e.stopPropagation(); setClickedMovieId(id); } return ( <div > <Grid container spacing={1}> {movies.map((movie) => ( <Grid item xs={6} sm={2} key={movie.id}> <Card className="card"> <CardActionArea> <CardMedia component="img" /> </CardActionArea> <CardActions> <Button size="small" color="primary"> <FavoriteBorderIcon /> </Button> <Button size="small" color="primary" onClick={(e) => handleMovieClick(e, movie.id)}> {movie.rating} </Button> </CardActions> </Card> </Grid> ))} </Grid> </div> ); } export default MovieCards;
内容的提问来源于stack exchange,提问作者rumon
相关产品推荐
相关产品推荐

