React项目中删除收藏电影后列表未实时更新问题求助
问题:React收藏电影列表删除后无法实时更新
开发了一个基于React.js的页面展示用户收藏的电影列表,用户可删除收藏的电影。点击删除按钮能成功将电影从服务器删除,但列表仅在刷新页面后才会移除对应卡片,而非实时更新。项目包含App、SavedMovies、MoviesCardList、MoviesCard组件,删除接口请求正常,尝试过禁用缓存、在SavedMovies组件中添加监听savedMovies状态的useEffect,但均无效果。相关组件代码如下:
function App() { const [currentUser, setCurrentUser] = useState({}); const [isLoggedIn, setIsLoggedIn] = useState(localStorage.getItem('token') ? true : false); const [savedMovies, setSavedMovies] = useState([]); React.useEffect(() => { if (isLoggedIn) { Promise.all([mainApi.getUserInfo(), mainApi.getSavedMovies()]) .then(([data, items]) => { const saved = items.filter((m) => m.owner === data._id); setCurrentUser(data); setSavedMovies(saved); }) .catch((err) => { console.log(err); }) } }, [isLoggedIn]) function handleDeleteMovie(movie) { const savedMovie = savedMovies.find((m) => { return m.movieId === (movie.id || movie.movieId) }) const newSavedMovies = savedMovies.filter((m) => m.movieId !== movie.id) mainApi.deleteMovie(savedMovie._id) .then(() => { setSavedMovies(newSavedMovies); }) .catch((err) => { console.log(err); }) } return ( <div className="app"> <CurrentUserContext.Provider value={currentUser}> <Routes> <Route path="saved-movies" element={ <ProtectedRoute isLoggedIn={isLoggedIn}> <Header isLoggedIn={isLoggedIn} /> <SavedMovies savedMovies={savedMovies} onDeleteMovie={handleDeleteMovie} /> <Footer /> </ProtectedRoute> } /> </Routes> </CurrentUserContext.Provider> </div> ); } function SavedMovies({ savedMovies, onDeleteMovie }) { const [isShortChecked, setIsShortChecked] = useState(false); const [initialMovies, setInitialMovies] = useState(savedMovies); const [searchResult, setSearchResult] = useState(initialMovies); function filterShortMovies(movies) { return movies.filter((movie) => movie.duration <= 40 ) } function handleFilter(query) { const result = savedMovies.filter((movie) => { return ( movie.nameRU.toLowerCase().includes(query.toLowerCase()) || movie.nameEN.toLowerCase().includes(query.toLowerCase()) ) }) setInitialMovies(result); setSearchResult(isShortChecked ? filterShortMovies(result) : result); } function handleShort() { setIsShortChecked(!isShortChecked); if (!isShortChecked) { setSearchResult(filterShortMovies(initialMovies)) } else { setSearchResult(initialMovies) } } function onSearchSubmit(inputData) { handleFilter(inputData) } useEffect(() => { setSearchResult(savedMovies) }, [savedMovies]) return ( <div className='saved-movies'> <SearchForm onSearch={onSearchSubmit} isShort={isShortChecked} handleShort={handleShort} /> <MoviesCardList savedMovies={savedMovies} onDeleteMovie={onDeleteMovie} movies={searchResult} /> </div> ) } function MoviesCard({ movie, onSaveMovie, onDeleteMovie, isSaved }) { const location = useLocation(); const [buttonText, setButtonText] = React.useState(''); const [buttonClassName, setButtonClassName] = React.useState(''); const [buttonVisibility, setButtonVisibility] = React.useState('movie-card__button_invisible'); React.useEffect(() => { if (isSaved) { setButtonText(''); setButtonClassName('movie-card__button-delete'); setButtonVisibility(''); if (location.pathname === '/saved-movies') { setButtonVisibility('movie-card__button_invisible'); } } else { setButtonText('Сохранить'); setButtonClassName('movie-card__button-save'); setButtonVisibility('movie-card__button_invisible'); } }, [isSaved] ) function handleButtonChange(evt) { evt.preventDefault(); if (isSaved) { onDeleteMovie(movie); setButtonClassName('movie-card__button-save'); setButtonText('Сохранить'); } else { onSaveMovie(movie); setButtonText(''); setButtonClassName('movie-card__button-delete'); } } function handleDelete(evt) { evt.preventDefault(); onDeleteMovie(movie); } return ( <div className='movie-card'> <a href={movie.trailerLink} className='movie-card__container' onMouseOver={handleMouseOver} onMouseLeave={handleMouseLeave} target="_blank" rel="noreferrer"> <img src={ } className='movie-card__image' /> { location.pathname === '/saved-movies' && ( <button className={`movie-card__button movie-card__button-remove ${buttonVisibility}`} onClick={handleDelete}></button> ) } </a> <div className='movie-card__description'> <p className='movie-card__name'>{movie.name}</p> <span className='movie-card__duration'>{`${hours} ${minutes}`}</span> </div> </div> ) }
问题分析与修复方案
1. 删除逻辑中的过滤条件错误
在App组件的handleDeleteMovie函数中,过滤新数组时使用的是m.movieId !== movie.id,但从SavedMovies传入的电影对象,其标识字段应该是movie.movieId(服务器返回的收藏电影结构),而非movie.id。这会导致过滤后的数组没有移除目标电影,状态更新后列表无变化。
修复代码:
function handleDeleteMovie(movie) { const savedMovie = savedMovies.find((m) => { return m.movieId === (movie.id || movie.movieId); }); // 统一获取目标电影的ID字段 const targetMovieId = movie.id || movie.movieId; const newSavedMovies = savedMovies.filter((m) => m.movieId !== targetMovieId); mainApi.deleteMovie(savedMovie._id) .then(() => { setSavedMovies(newSavedMovies); }) .catch((err) => { console.log(err); }); }
2. SavedMovies组件状态不同步
SavedMovies中的initialMovies通过useState(savedMovies)初始化,仅在组件挂载时赋值一次,后续savedMovies更新时不会自动同步。而页面展示的searchResult依赖initialMovies,导致删除后searchResult仍基于旧数据渲染。
修复代码:
function SavedMovies({ savedMovies, onDeleteMovie }) { const [isShortChecked, setIsShortChecked] = useState(false); const [initialMovies, setInitialMovies] = useState(savedMovies); const [searchResult, setSearchResult] = useState(initialMovies); // 监听savedMovies和isShortChecked变化,同步更新状态 useEffect(() => { setInitialMovies(savedMovies); // 根据当前筛选状态更新搜索结果 setSearchResult(isShortChecked ? filterShortMovies(savedMovies) : savedMovies); }, [savedMovies, isShortChecked]); // ...其余代码保持不变 }
3. 验证删除按钮的参数传递
确保MoviesCard中点击删除按钮时,传递给onDeleteMovie的movie对象包含正确的movieId或id字段,避免因字段缺失导致过滤逻辑失效。
额外排查建议
- 删除成功后,在
handleDeleteMovie的then回调中打印newSavedMovies和原savedMovies,对比是否正确移除了目标电影。 - 检查
SavedMovies中searchResult的更新逻辑,确保任何状态变化都能同步到最终展示的列表中。
内容的提问来源于stack exchange,提问作者Rubyroido
相关产品推荐
相关产品推荐

