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

React结合Bootstrap实现点击按钮切换API数据的方法

React + Bootstrap 实现点击按钮切换 SpaceX 数据

看起来你已经有了本地过滤的思路,结合你说的「所有数据均来自API」,我给你整理了两种常用的实现方案,你可以根据需求选择:


方案一:先获取全量数据,本地切换过滤(减少API请求次数)

这种方式会在组件挂载时一次性拉取所有100条数据,之后点击按钮只在本地过滤,适合数据不会频繁更新的场景:

步骤拆解

  1. 初始化组件状态:需要存储全量数据、过滤后的数据,以及加载状态
  2. 组件挂载时请求全量数据:调用不带launch_success参数的API
  3. 完善你的过滤方法:根据点击的launch值过滤全量数据,更新展示用的状态
  4. 用Bootstrap按钮组实现切换UI:给选中的按钮添加激活样式
  5. 渲染数据列表:处理加载状态和空数据情况

完整代码示例

import React, { Component } from 'react';
// 可以直接在public/index.html引入Bootstrap CSS:
// <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">

class SpaceXLaunches extends Component {
  constructor(props) {
    super(props);
    this.state = {
      allPosts: [],
      filteredPosts: [],
      loading: true,
      activeFilter: null // 记录当前选中的过滤值
    };
  }

  // 组件挂载时获取全量数据
  componentDidMount() {
    fetch('https://api.spaceXdata.com/v3/launches?limit=100')
      .then(res => res.json())
      .then(data => {
        this.setState({
          allPosts: data,
          filteredPosts: data, // 默认展示全部数据
          loading: false
        });
      })
      .catch(err => {
        console.error('获取数据失败:', err);
        this.setState({ loading: false });
      });
  }

  // 你的过滤方法优化版
  filterByLaunch = (launch) => {
    let filtered;
    if (launch === null) {
      // 可选:添加「全部」按钮的逻辑
      filtered = this.state.allPosts;
    } else {
      filtered = this.state.allPosts.filter(item => item.launch_success === launch);
    }
    this.setState({
      filteredPosts: filtered,
      activeFilter: launch
    });
  };

  render() {
    const { filteredPosts, loading, activeFilter } = this.state;

    if (loading) {
      return <div className="text-center mt-5">加载中...</div>;
    }

    return (
      <div className="container mt-4">
        {/* Bootstrap按钮组 */}
        <div className="btn-group mb-4" role="group">
          <button
            className={`btn btn-primary ${activeFilter === null ? 'active' : ''}`}
            onClick={() => this.filterByLaunch(null)}
          >
            全部
          </button>
          <button
            className={`btn btn-primary ${activeFilter === true ? 'active' : ''}`}
            onClick={() => this.filterByLaunch(true)}
          >
            成功发射
          </button>
          <button
            className={`btn btn-primary ${activeFilter === false ? 'active' : ''}`}
            onClick={() => this.filterByLaunch(false)}
          >
            发射失败
          </button>
        </div>

        {/* 数据列表 */}
        <div className="row">
          {filteredPosts.length === 0 ? (
            <div className="col-12 text-center">暂无数据</div>
          ) : (
            filteredPosts.map(post => (
              <div key={post.flight_number} className="col-md-4 mb-3">
                <div className="card">
                  <img
                    src={post.links.mission_patch_small || 'https://via.placeholder.com/200'}
                    className="card-img-top"
                    alt={`Mission ${post.mission_name}`}
                  />
                  <div className="card-body">
                    <h5 className="card-title">{post.mission_name}</h5>
                    <p className="card-text">
                      发射状态:{post.launch_success ? '✅ 成功' : '❌ 失败'}
                    </p>
                    <p className="card-text">
                      发射日期:{new Date(post.launch_date_local).toLocaleDateString()}
                    </p>
                  </div>
                </div>
              </div>
            ))
          )}
        </div>
      </div>
    );
  }
}

export default SpaceXLaunches;

方案二:每次点击按钮请求对应API(实时获取最新数据)

如果需要确保每次点击都拉取最新的API数据,可以选择这种方式,每次点击时带上launch_success参数请求:

步骤拆解

  1. 初始化组件状态:存储当前数据、加载状态、当前选中的过滤值
  2. 封装API请求方法:根据传入的launch参数拼接URL,发起请求
  3. 按钮点击触发请求:点击不同按钮时传入对应的true/false/null(全部)
  4. 渲染UI和数据:同样处理加载状态和空数据

完整代码示例

import React, { Component } from 'react';

class SpaceXLaunches extends Component {
  constructor(props) {
    super(props);
    this.state = {
      posts: [],
      loading: true,
      activeFilter: null
    };
  }

  // 组件挂载时默认请求全部数据
  componentDidMount() {
    this.fetchLaunches(null);
  }

  // 封装API请求方法
  fetchLaunches = (launch) => {
    this.setState({ loading: true });
    let url = 'https://api.spaceXdata.com/v3/launches?limit=100';
    if (launch !== null) {
      url += `&launch_success=${launch}`;
    }

    fetch(url)
      .then(res => res.json())
      .then(data => {
        this.setState({
          posts: data,
          activeFilter: launch,
          loading: false
        });
      })
      .catch(err => {
        console.error('获取数据失败:', err);
        this.setState({ loading: false });
      });
  };

  render() {
    const { posts, loading, activeFilter } = this.state;

    if (loading) {
      return <div className="text-center mt-5">加载中...</div>;
    }

    return (
      <div className="container mt-4">
        <div className="btn-group mb-4" role="group">
          <button
            className={`btn btn-primary ${activeFilter === null ? 'active' : ''}`}
            onClick={() => this.fetchLaunches(null)}
          >
            全部
          </button>
          <button
            className={`btn btn-primary ${activeFilter === true ? 'active' : ''}`}
            onClick={() => this.fetchLaunches(true)}
          >
            成功发射
          </button>
          <button
            className={`btn btn-primary ${activeFilter === false ? 'active' : ''}`}
            onClick={() => this.fetchLaunches(false)}
          >
            发射失败
          </button>
        </div>

        <div className="row">
          {posts.length === 0 ? (
            <div className="col-12 text-center">暂无数据</div>
          ) : (
            posts.map(post => (
              <div key={post.flight_number} className="col-md-4 mb-3">
                <div className="card">
                  <img
                    src={post.links.mission_patch_small || 'https://via.placeholder.com/200'}
                    className="card-img-top"
                    alt={`Mission ${post.mission_name}`}
                  />
                  <div className="card-body">
                    <h5 className="card-title">{post.mission_name}</h5>
                    <p className="card-text">
                      发射状态:{post.launch_success ? '✅ 成功' : '❌ 失败'}
                    </p>
                    <p className="card-text">
                      发射日期:{new Date(post.launch_date_local).toLocaleDateString()}
                    </p>
                  </div>
                </div>
              </div>
            ))
          )}
        </div>
      </div>
    );
  }
}

export default SpaceXLaunches;

小提示

  • 如果你使用react-bootstrap组件库,可以把原生<button>替换成Button组件,样式会更统一
  • 给图片加了默认占位图,避免因图片链接失效导致页面空白
  • 可以根据需求添加更多过滤条件(比如着陆状态),逻辑和上面一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 18:22:38