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

React星舰项目点击Load More按钮后图片不更新的问题求助

问题分析与解决方案

Hey there! Let's break down why your starship images aren't updating properly and fix it step by step.

核心问题原因

  1. 数据被重复清空,导致图片索引重复
    你的getFilms方法一开始就执行this.setState({ data: [] }),会把之前加载的所有星舰数据清空,然后重新加载新页面的数据。这就导致新数据的索引总是从0开始,每次都复用posterImages的前几项,看起来图片完全没变化。

  2. 依赖数组index的不可靠性
    用map循环的index来匹配图片,不仅会在数据追加时超出你定义的图片数组长度,还会因为React复用index作为key的DOM元素,导致图片无法正确更新。

  3. 多次setState的低效与风险
    在data[0].map里逐个调用setState添加星舰数据,会触发组件多次重复渲染,既低效又可能引发状态更新的意外冲突。


分步解决方法

1. 修改数据加载逻辑,实现追加而非替换

去掉清空data的语句,一次性将新页面的数据追加到现有state中,同时优化异步请求的写法:

async getFilms() {
  const { page, data } = this.state;
  this.setState({ isLoading: true }); // 开启加载状态

  try {
    const res = await fetch(`https://swapi.dev/api/starships/?page=${page}`);
    const response = await res.json();
    // 追加新数据到现有列表
    this.setState({
      data: [...data, ...response.results],
      isLoading: false
    });
  } catch (err) {
    console.error("Failed to load starships:", err);
    this.setState({ isLoading: false });
  }
}

2. 用星舰名称映射图片,摆脱index依赖

将posterImages从数组改为对象,用星舰名称作为key,确保每个星舰能匹配到固定的图片:

posterImages = {
  "CR90 corvette": "CR90-Corvette.png",
  "Star Destroyer": "stardestroyer.png",
  "Sentinel-class landing craft": "Sentinel-class-landing-craft.jpg",
  "Death Star": "Death-Star.png",
  "Millennium Falcon": "Millenium_Falcon.jpg",
  "Y-wing": "Y-wing.png",
  "X-wing": "x-wing.png",
  "TIE Advanced x1": "TIE-Advanced-x1.png",
  "Executor": "executor.png",
  "Rebel transport": "rebel-transport.png"
};

然后在render中根据星舰名称获取图片,同时用星舰的url作为唯一key(比index更可靠):

<li 
  key={res.url}
  className="ship-list" 
  style={{
    backgroundImage: `url(${this.posterImages[res.name] || './default-starship.png'})`
    // 加入默认图片,避免找不到对应图片时显示空白
  }}
>
  <div className="name">{res.name}</div>
</li>

3. 修复废弃的生命周期方法

componentWillMount已经在React 17+被废弃,替换为componentDidMount来初始化数据:

componentDidMount() {
  this.getFilms();
}

4. 优化组件初始状态

确保初始的isLoading状态合理,避免初始渲染时显示错误的按钮文本:

state = {
  data: [],
  movieDetailsOpen: false,
  movieSelected: "",
  index: 0,
  page: 1,
  isLoading: true, // 初始加载时设为true
};

完整修改后的App.js代码

import React, { Component } from 'react';
import "../styles/Films.css";

class Films extends Component {
  state = {
    data: [],
    movieDetailsOpen: false,
    movieSelected: "",
    index: 0,
    page: 1,
    isLoading: true,
  };

  posterImages = {
    "CR90 corvette": "CR90-Corvette.png",
    "Star Destroyer": "stardestroyer.png",
    "Sentinel-class landing craft": "Sentinel-class-landing-craft.jpg",
    "Death Star": "Death-Star.png",
    "Millennium Falcon": "Millenium_Falcon.jpg",
    "Y-wing": "Y-wing.png",
    "X-wing": "x-wing.png",
    "TIE Advanced x1": "TIE-Advanced-x1.png",
    "Executor": "executor.png",
    "Rebel transport": "rebel-transport.png"
  };

  async getFilms() {
    const { page, data } = this.state;
    this.setState({ isLoading: true });

    try {
      const res = await fetch(`https://swapi.dev/api/starships/?page=${page}`);
      const response = await res.json();
      this.setState({
        data: [...data, ...response.results],
        isLoading: false
      });
    } catch (err) {
      console.error("Failed to load starships:", err);
      this.setState({ isLoading: false });
    }
  }

  componentDidMount() {
    this.getFilms();
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.page !== this.state.page) {
      this.getFilms();
    }
  }

  loadMore = () => {
    this.setState((prevState) => ({ page: prevState.page + 1 }));
  };

  render() {
    const { isLoading, data } = this.state;
    return (
      <div className="starships-wrapper">
        <div className="top" id="top">
          <img src="./logo.png" alt="logo"/>
        </div>
        <div className="bottom" id="bottom">
          <ul>
            {data.map((res) => (
              <li 
                key={res.url}
                className="ship-list" 
                style={{
                  backgroundImage: `url(${this.posterImages[res.name] || './default-starship.png'})`
                }}
              >
                <div className="name">{res.name}</div>
              </li>
            ))}
          </ul>
        </div>
        <div className="load-more">
          <button onClick={this.loadMore} className="btn-grad" variant="dark" disabled={isLoading}>
            {isLoading ? 'Loading...' : 'Load More'}
          </button>
        </div>
      </div>
    )
  }
}

export default Films;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:17:39