React中如何为4个数据端点实现无整页刷新的刷新按钮
实现方案
按以下步骤改造即可实现点击按钮刷新4个端点数据,不会触发整页重载:
1. 重构App.js数据拉取逻辑
把分散在4个useEffect中的请求逻辑抽成统一的拉取方法,用Promise.all并行请求所有接口,同时暴露刷新方法传给子组件:
import { useState, useEffect, useCallback } from 'react'; import Tables from './Table'; function App() { const [feed, setFeed] = useState({}); const [feedol, setFeedol] = useState({}); const [jsonData, setJsonData] = useState({}); const [azureData, setAzureData] = useState({}); const [isRefreshing, setIsRefreshing] = useState(false); // 统一封装全量数据拉取逻辑 const fetchAllData = useCallback(async () => { setIsRefreshing(true); try { // 并行拉取4个接口,减少等待时间 const [feedRes, feedolRes, jsonRes, azureRes] = await Promise.all([ fetch("/feed").then(res => res.json()), fetch("/feed_ol").then(res => res.json()), fetch("/json_data").then(res => res.json()), fetch("/azure_data").then(res => res.json()) ]); // 批量更新状态,仅触发一次重渲染 setFeed(feedRes); setFeedol(feedolRes); setJsonData(jsonRes["archive"][0]); setAzureData(azureRes); } catch (err) { console.error("数据拉取失败:", err); } finally { setIsRefreshing(false); } }, []); // 组件首次挂载时拉取初始数据 useEffect(() => { fetchAllData(); }, [fetchAllData]); return ( <div className="App"> <Tables feed={feed} feedol={feedol} jsonData={jsonData} azureData={azureData} refresh={fetchAllData} isRefreshing={isRefreshing} /> </div> ); } export default App;
注意:初始state建议设为空对象
{}而不是空数组[],避免首次渲染时读取feed.title这类属性报undefined错误。
2. 修改Table.js按钮绑定
把父组件传入的刷新方法绑定到按钮的点击事件上,同时加上加载状态的交互:
import React from "react"; import { Card, Container } from "react-bootstrap"; import { BsArrowClockwise } from "react-icons/bs"; import Cards from "./Cards"; import "./tables.css"; export const Tables = ({ feed, feedol, jsonData, azureData, refresh, isRefreshing }) => { return ( <Container> <Cards /> <Card className="table-card"> <Card.Header> {" "} <button type="button" className="refresh-button" onClick={refresh} disabled={isRefreshing} > {" "} <BsArrowClockwise className={isRefreshing ? "spin" : ""} /> </button>{" "} OneLogin Outages{" "} </Card.Header> <Card.Body> <table className="table table-bordered"> <thead> <tr> <th>Title</th> <th>Link</th> <th>Date</th> </tr> </thead> <tbody> <tr> <td>{feedol.title}</td> <td>{feedol.link}</td> <td>{feedol.updated}</td> </tr> <tr> <td>{}</td> <td>{}</td> <td>{}</td> </tr> <tr> <td>{jsonData.service_name}</td> <td>{jsonData.summary}</td> <td>{jsonData.date}</td> </tr> </tbody> </table> </Card.Body> </Card> <Cards /> <Card className="table-card"> <Card.Header> Unifi Outages </Card.Header> <Card.Body> <table className="table table-bordered"> <thead> <tr> <th>Title</th> <th>Link</th> <th>Date</th> </tr> </thead> <tbody> <tr> <td>{feed.title}</td> <td>{feed.link}</td> <td>{feed.updated}</td> </tr> <tr> <td>{}</td> <td>{}</td> <td>{}</td> </tr> <tr> <td>{azureData.title}</td> <td>{azureData.link}</td> <td>{azureData.updated}</td> </tr> </tbody> </table> </Card.Body> </Card> </Container> ); };
如果需要刷新时图标旋转,在tables.css里加以下样式即可:
.spin { animation: spin 1s linear infinite; } @keyframes spin { from {transform: rotate(0deg);} to {transform: rotate(360deg);} }
避坑说明
- 按钮必须显式添加
type="button",否则在部分场景下会默认触发表单提交导致页面刷新,这是之前出现整页刷新的常见原因之一 - 所有数据更新走React状态驱动,只会重渲染依赖对应state的组件部分,不会触发整页重载
- 用
Promise.all并行请求接口比原来4个独立useEffect串行发请求速度更快,批量更新state也不会造成多次无意义重渲染 - 如果后续要加定时器自动刷新,直接在useEffect里写
setInterval(fetchAllData, 刷新间隔)即可,不要用window.location.reload()这类硬刷新方法
内容的提问来源于stack exchange,提问作者Carolina D Vallecillo
相关产品推荐
相关产品推荐

