React中基于useState状态的API调用的正确实现方式
正确发起基于useState状态的API调用的方法
当依赖useState管理的状态发起API调用时,直接在函数中使用状态值可能因为闭包捕获旧值的问题,拿不到最新状态。你给出的示例里,loadCountryData1存在这样的风险,loadCountryData2虽然能拿到最新值,但属于滥用setState回调的取巧写法,不符合官方规范。以下是几种官方推荐的正确实现方式:
1. 用useRef跟踪最新状态
useRef的.current属性不受闭包影响,始终保存最新的状态值,适合需要确保拿到最新状态的场景:
import { useState, useRef } from "react"; import axios from "axios"; import "./styles.css"; export default function App() { const [selected, setSelected] = useState(null); const selectedRef = useRef(null); // 创建ref跟踪最新选中值 const handleSelect = (e) => { const value = e.currentTarget.value; setSelected(value); selectedRef.current = value; // 同步更新ref的值 }; const [countryData, setCountryData] = useState(null); const loadCountryData = () => { // 使用ref中的最新值发起请求 axios.get(`https://restcountries.com/v3.1/name/${selectedRef.current}`) .then((response) => setCountryData(response.data)); }; return ( <div className="App"> <div className="container"> <select value={selected} onChange={handleSelect}> <option value={null}>Select country</option> <option value="india">India</option> <option value="usa">USA</option> <option value="germany">Germany</option> </select> <button onClick={loadCountryData} disabled={selected === null}> Load </button> </div> <div> {countryData !== null && ( <pre>{JSON.stringify(countryData, null, 4)}</pre> )} </div> </div> ); }
2. 事件触发时直接传递当前状态值
如果是通过按钮点击手动触发API调用,可以在点击回调中直接把当前状态作为参数传入。由于组件每次状态更新后都会重新渲染,点击回调会捕获最新的状态值:
// 修改按钮的onClick回调,直接传入当前selected <button onClick={() => loadCountryData(selected)} disabled={selected === null}> Load </button> // 对应的loadCountryData函数接收参数 const loadCountryData = (currentSelected) => { axios.get(`https://restcountries.com/v3.1/name/${currentSelected}`) .then((response) => setCountryData(response.data)); };
3. 用useEffect监听状态变化(自动触发场景)
如果需求是状态更新后自动加载数据,而非手动点击按钮,useEffect是最标准的实现方式。React会确保useEffect的回调在状态更新后执行,且使用最新的状态值:
import { useState, useEffect } from "react"; import axios from "axios"; import "./styles.css"; export default function App() { const [selected, setSelected] = useState(null); const handleSelect = (e) => { setSelected(e.currentTarget.value); }; const [countryData, setCountryData] = useState(null); // 监听selected变化,自动发起请求 useEffect(() => { if (!selected) return; // 未选中时不执行 const fetchCountryData = async () => { try { const response = await axios.get(`https://restcountries.com/v3.1/name/${selected}`); setCountryData(response.data); } catch (err) { console.error("加载数据失败:", err); } }; fetchCountryData(); }, [selected]); // 依赖项为selected,状态变化时触发 return ( <div className="App"> <div className="container"> <select value={selected} onChange={handleSelect}> <option value={null}>Select country</option> <option value="india">India</option> <option value="usa">USA</option> <option value="germany">Germany</option> </select> </div> <div> {countryData !== null && ( <pre>{JSON.stringify(countryData, null, 4)}</pre> )} </div> </div> ); }
补充说明
- 为什么
loadCountryData1不安全:JavaScript闭包会捕获函数定义时的状态值,若在setSelected后立即调用该函数(比如快速操作或同步调用),此时组件还未重新渲染,函数内的selected仍是旧值,导致API调用使用错误参数。 - 为什么
loadCountryData2不推荐:setState的回调函数设计目的是计算新状态,而非执行副作用(如API调用),这种写法违反React设计规范,代码可读性差且易引发维护问题。
内容的提问来源于stack exchange,提问作者Bob Sacamano
相关产品推荐
相关产品推荐

