You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 18:25:28