React中useState添加电影标题失效,useEffect始终显示空数组求助
React电影标题数组添加问题解决方案
核心问题就是你怀疑的页面跳转导致状态丢失:点击<Link>后,页面会立即跳转到详情页,当前的AllFilms组件会被卸载,selectedFilms状态直接重置,所以useEffect打印空数组,UI也没法展示。下面给你两种可行的解决办法:
办法1:先更新状态再手动跳转
不用<Link>自带的跳转,改用useNavigate手动控制跳转时机,确保状态更新完成后再跳转:
import {useEffect, useState, useNavigate} from 'react'; import {OneFilm} from "./OneFilm"; import {Film} from "../../interfaces"; import styles from '../../css/AllFilms.module.css'; export function AllFilms() { const [allFilms, setAllFilms] = useState<Film[]>([]); const [selectedFilms, setSelectedFilms] = useState<string[]>([]) const navigate = useNavigate(); // 初始化导航钩子 const fetchFilms = async () => { try { const response = await fetch(`URL`, { headers: { 'X-API-KEY': process.env.REACT_APP_TOKEN } }); const dataResp = await response.json(); setAllFilms(dataResp); } catch (error) { console.error('Ошибка при получении фильмов:', error); } }; const selectFilm = (filmTitle: string, filmId: string) => { setSelectedFilms(prev => [...prev, filmTitle]); // 延迟跳转,确保状态更新完成 setTimeout(() => { navigate(`/id=${filmId}`); }, 0); } useEffect(() => { fetchFilms() }, []) useEffect(() => { console.log(selectedFilms) }, [selectedFilms]) return ( <div className={styles.container}> <ul className={styles.films}> { allFilms.map((film) => ( <div key={film.id} onClick={() => selectFilm(film.name, film.id)}> <OneFilm film={film}/> </div> ) )} </ul> <div className="selected__films"> <ul> {selectedFilms.map((film, index) => ( <li key={index}>{film}</li> ))} </ul> </div> </div> ); }
办法2:用全局状态管理(跨页面保留选中列表)
如果需要跳转后依然保留选中的电影列表,就得把状态放到全局,比如用React Context:
第一步:创建全局状态上下文
// FilmContext.tsx import { createContext, useContext, useState, ReactNode } from 'react'; type FilmContextType = { selectedFilms: string[]; addSelectedFilm: (title: string) => void; }; const FilmContext = createContext<FilmContextType | undefined>(undefined); export function FilmProvider({ children }: { children: ReactNode }) { const [selectedFilms, setSelectedFilms] = useState<string[]>([]); const addSelectedFilm = (title: string) => { setSelectedFilms(prev => [...prev, title]); }; return ( <FilmContext.Provider value={{ selectedFilms, addSelectedFilm }}> {children} </FilmContext.Provider> ); } export function useFilmContext() { const context = useContext(FilmContext); if (!context) { throw new Error('useFilmContext必须在FilmProvider内部使用'); } return context; }
第二步:用Provider包裹路由
在App.tsx里把路由组件用FilmProvider包起来:
import { FilmProvider } from './components/FilmContext'; import { Routes, Route } from 'react-router-dom'; import { AllFilms } from './components/AllFilms'; import { OneFilmDetail } from './components/OneFilmDetail'; // 你的详情页组件 function App() { return ( <FilmProvider> <Routes> <Route path="/" element={<AllFilms />} /> <Route path="/id=:id" element={<OneFilmDetail />} /> </Routes> </FilmProvider> ); }
第三步:修改AllFilms组件使用全局状态
import {useEffect, useState} from 'react'; import {OneFilm} from "./OneFilm"; import {Link} from 'react-router-dom'; import {Film} from "../../interfaces"; import styles from '../../css/AllFilms.module.css'; import { useFilmContext } from './FilmContext'; export function AllFilms() { const [allFilms, setAllFilms] = useState<Film[]>([]); const { selectedFilms, addSelectedFilm } = useFilmContext(); const fetchFilms = async () => { try { const response = await fetch(`URL`, { headers: { 'X-API-KEY': process.env.REACT_APP_TOKEN } }); const dataResp = await response.json(); setAllFilms(dataResp); } catch (error) { console.error('Ошибка при получении фильмов:', error); } }; const selectFilm = (filmTitle: string) => { addSelectedFilm(filmTitle); } useEffect(() => { fetchFilms() }, []) useEffect(() => { console.log(selectedFilms) }, [selectedFilms]) return ( <div className={styles.container}> <ul className={styles.films}> { allFilms.map((film) => ( <Link to={`/id=${film.id}`} key={film.id} onClick={() => selectFilm(film.name)}> <OneFilm film={film}/> </Link> ) )} </ul> <div className="selected__films"> <ul> {selectedFilms.map((film, index) => ( <li key={index}>{film}</li> ))} </ul> </div> </div> ); }
补充说明
- 办法1适合只需要当前页面保留选中状态的场景,办法2适合跨页面共享选中列表的需求。
- 用
<Link>时,点击事件的执行顺序是:先触发你的selectFilm,然后立即执行跳转,组件直接卸载,导致状态还没更新就没了,这就是为什么alert能触发但数组是空的。
内容的提问来源于stack exchange,提问作者VueFrontend
相关产品推荐
相关产品推荐

