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

React中调用API的最优方式?封装API文件返回数据为undefined如何解决

核心问题

你当前的写法违反了React Hook的使用规则:useState、useEffect这类Hook只能在React函数组件内部,或者以use开头的自定义Hook函数中调用,不能在普通JS对象的方法里使用,这是你无法正常拿到返回值的根本原因。
同时你的API封装逻辑和UI层状态逻辑耦合,不符合分层设计原则,合理的实现应该把纯接口请求逻辑和React状态处理分开。


改造方案

1. 重写API封装层(Movie.js)

API文件只负责封装纯请求逻辑,直接返回Promise,不处理任何React相关状态:

import axios from 'axios';

const apiKey = 'apikey';
const url = 'https://api.themoviedb.org/3';

const Movie = {
  // 直接返回axios请求的Promise对象
  GetMovieTrends() {
    return axios.get(`${url}/trending/movie/day?api_key=${apiKey}`)
      .then(response => response.data)
      .catch(err => {
        console.log(err);
        throw err;
      });
  },
  GetMovieGenres() {
    return axios.get(`${url}/genre/movie/list?api_key=${apiKey}`)
      .then(response => response.data.genres)
      .catch(err => {
        console.log(err);
        throw err;
      });
  }
};

export default Movie;

2. 方案一:组件内处理请求状态(适合单组件使用)

直接在业务组件内用Hook处理请求、状态和渲染逻辑:

import React, {useState, useEffect} from "react";
import MovieImage from "./movie/MovieImage";
import MovieTextContainer from "./movie/MovieTextContainer";
import Movie from '../models/Movie.js';
import CircularProgress from '@material-ui/core/CircularProgress';
import { withStyles } from '@material-ui/core/styles';

const WaitForMovie = withStyles(() => ({
  root: {
    color: '#212121',
    width: '64px !important',
    height: '64px !important',
  },
}))(CircularProgress);

const TrendContainer = () => {
  // 声明状态管理电影数据和加载状态
  const [movieTrends, setMovieTrends] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const data = await Movie.GetMovieTrends();
        setMovieTrends(data.results || []);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

  return (
    <div>
      <h1 className='heading1 bottom-margin'>Movie Trending</h1>
      <div className='container-movie'>
        {loading ? (
          <WaitForMovie />
        ) : (
          movieTrends.slice(0, 7).map((movie) => (
            <div className='wrap-card-movie bottom-margin' key={movie.id}>
              <MovieImage 
                url={`https://image.tmdb.org/t/p/original/${movie.poster_path}`}
                name={movie.title}
              />
              <MovieTextContainer 
                title={movie.original_title}
                idGenre={movie.genre_ids}
              />
            </div>
          ))
        )}
      </div>
    </div>
  );
};

export default TrendContainer;

3. 方案二:抽离自定义Hook(适合多组件复用请求逻辑)

如果多个组件都要用到电影相关的接口数据,可以把请求逻辑抽成自定义Hook复用:

// 新建hooks/useMovie.js文件
import {useState, useEffect} from 'react';
import Movie from '../models/Movie.js';

// 自定义Hook必须以use开头
export const useMovieTrends = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const res = await Movie.GetMovieTrends();
        setData(res.results || []);
        setError(null);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

  return {data, loading, error};
};

export const useMovieGenres = () => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const res = await Movie.GetMovieGenres();
        setData(res || []);
        setError(null);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

  return {data, loading, error};
};

在组件内直接调用自定义Hook即可:

// TrendContainer.jsx中使用
import { useMovieTrends } from '../hooks/useMovie.js';

const TrendContainer = () => {
  const {data: movieTrends, loading} = useMovieTrends();
  // 剩下的渲染逻辑和方案一一致
}

内容的提问来源于stack exchange,提问作者Kkzio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 12:48:05