React结合Bootstrap实现点击按钮切换API数据的方法
React + Bootstrap 实现点击按钮切换 SpaceX 数据
看起来你已经有了本地过滤的思路,结合你说的「所有数据均来自API」,我给你整理了两种常用的实现方案,你可以根据需求选择:
方案一:先获取全量数据,本地切换过滤(减少API请求次数)
这种方式会在组件挂载时一次性拉取所有100条数据,之后点击按钮只在本地过滤,适合数据不会频繁更新的场景:
步骤拆解
- 初始化组件状态:需要存储全量数据、过滤后的数据,以及加载状态
- 组件挂载时请求全量数据:调用不带
launch_success参数的API - 完善你的过滤方法:根据点击的
launch值过滤全量数据,更新展示用的状态 - 用Bootstrap按钮组实现切换UI:给选中的按钮添加激活样式
- 渲染数据列表:处理加载状态和空数据情况
完整代码示例
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参数请求:
步骤拆解
- 初始化组件状态:存储当前数据、加载状态、当前选中的过滤值
- 封装API请求方法:根据传入的
launch参数拼接URL,发起请求 - 按钮点击触发请求:点击不同按钮时传入对应的
true/false/null(全部) - 渲染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
相关产品推荐
相关产品推荐

