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

React中useReducer结合Axios获取数据异常的问题求助

React useReducer 异步请求问题解决方案

问题重现

你尝试在useReducer的reducer中直接调用异步API请求,虽然回调里能打印数据,但最终存入state的是空对象,渲染时无法正常显示内容。你的代码如下:

import React, { useReducer } from "react";
import axios from "axios";

export const ACTIONS = {
  ADD_STANDARD: 'add-to-compare',
  REMOVE_STANDARD: 'remove-from-compare'
}

function reducer(standards, action) {
  switch(action.type) {
    case(ACTIONS.ADD_STANDARD):
      return [...standards, addCompare(action.payload.standard)]
    case(ACTIONS.REMOVE_STANDARD):
      return standards.filter(item => item.id !== action.payload.standard)
    default:
      return 'Nothing to add'
  }
}

function addCompare( standard ) {
  return axios
    .get("https://the-url.com" + standard)
    .then((res) => {
      console.log(res.data)

      return {
        key: res.data.id,
        title: res.data.title.rendered
      }
    })
    .catch((err) => console.log(err));
}

export default function EntryStandards() {
  const [standards, dispatch] = useReducer(reducer, []);

  const addStandards = function(id) {
    dispatch({ type: ACTIONS.ADD_STANDARD, payload: {standard: id}})
  }

  return (
    <>
      <button onClick={() => addStandards(9603)}>Add 9603!</button>
      <button onClick={() => addStandards(9567)}>Add 9567!</button>
      <button onClick={() => addStandards(9531)}>Add 9531!</button>
      <button onClick={() => addStandards(9519)}>Add 9519!</button>

      {standards.map(standard => {
        return <p><button onClick={() => dispatch({ type: ACTIONS.REMOVE_STANDARD, payload: { standard: standard.id } })}>X</button> { standard.title } - { standard.version }</p>
      })}
    </>
  )
}

问题核心原因

  • reducer必须是纯函数:它只能同步处理state更新,不能包含任何异步操作(比如API请求、定时器)。你的代码在reducer里调用了addCompare,这个函数返回的是Promise对象,而不是实际的数据,所以state里存的是Promise,不是你需要的标准对象。
  • 渲染时无法读取Promise的属性:当你用standards.map遍历的时候,每个standard是Promise,自然读不到title、id这些属性,看起来就像“空对象”。

优化实现方案

把异步请求移到组件的事件处理函数中,等请求完成后再dispatch action更新state:

修改后的代码

import React, { useReducer } from "react";
import axios from "axios";

export const ACTIONS = {
  ADD_STANDARD: 'add-to-compare',
  REMOVE_STANDARD: 'remove-from-compare'
}

// reducer保持纯函数,只处理同步的state更新
function reducer(standards, action) {
  switch(action.type) {
    case ACTIONS.ADD_STANDARD:
      // 这里直接接收请求好的数据,存入state
      return [...standards, action.payload.standard]
    case ACTIONS.REMOVE_STANDARD:
      return standards.filter(item => item.id !== action.payload.standardId)
    default:
      return standards
  }
}

// 单独封装API请求函数
async function fetchStandard(standardId) {
  try {
    const res = await axios.get(`https://the-url.com${standardId}`);
    return {
      id: res.data.id, // 修正字段名,确保删除时能匹配
      title: res.data.title.rendered
      // 如果有version字段,这里补充对应取值逻辑
    }
  } catch (err) {
    console.error('请求标准数据失败:', err);
    return null;
  }
}

export default function EntryStandards() {
  const [standards, dispatch] = useReducer(reducer, []);

  const addStandards = async function(id) {
    // 先请求数据
    const standardData = await fetchStandard(id);
    // 请求成功再dispatch更新state
    if (standardData) {
      dispatch({ 
        type: ACTIONS.ADD_STANDARD, 
        payload: { standard: standardData }
      });
    }
  }

  return (
    <>
      <button onClick={() => addStandards(9603)}>Add 9603!</button>
      <button onClick={() => addStandards(9567)}>Add 9567!</button>
      <button onClick={() => addStandards(9531)}>Add 9531!</button>
      <button onClick={() => addStandards(9519)}>Add 9519!</button>

      {standards.map(standard => {
        return (
          <p key={standard.id}>
            <button onClick={() => dispatch({ 
              type: ACTIONS.REMOVE_STANDARD, 
              payload: { standardId: standard.id } 
            })}>X</button> 
            { standard.title }
            {/* 如果有version字段,这里加上 { standard.version } */}
          </p>
        )
      })}
    </>
  )
}

关键改动说明

  1. reducer回归纯函数:只负责接收已获取到的数据,同步更新state,不再处理任何异步逻辑。
  2. 异步请求移至事件处理函数:点击按钮后先调用API获取数据,拿到结果后再dispatch action。
  3. 修正数据字段一致性:把key改成id,确保删除时能正确匹配;同时给列表项加上key属性,符合React列表渲染要求。
  4. 增加错误处理:请求失败时打印错误并避免存入无效数据。

可选优化:添加加载状态

如果请求耗时较长,可以给按钮添加加载状态,避免重复点击:

import React, { useReducer, useState } from "react";
// ... 其他代码不变

export default function EntryStandards() {
  const [standards, dispatch] = useReducer(reducer, []);
  const [loadingIds, setLoadingIds] = useState([]);

  const addStandards = async function(id) {
    if (loadingIds.includes(id)) return;
    setLoadingIds(prev => [...prev, id]);
    const standardData = await fetchStandard(id);
    setLoadingIds(prev => prev.filter(item => item !== id));
    if (standardData) {
      dispatch({ 
        type: ACTIONS.ADD_STANDARD, 
        payload: { standard: standardData }
      });
    }
  }

  return (
    <>
      <button 
        onClick={() => addStandards(9603)} 
        disabled={loadingIds.includes(9603)}
      >
        {loadingIds.includes(9603) ? '加载中...' : 'Add 9603!'}
      </button>
      {/* 其他按钮同理修改 */}
      
      {/* 列表渲染部分不变 */}
    </>
  )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:54:28