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

React+Redux Toolkit渲染API数据出现.slice is not a function报错

问题根因排查

.slice() is not a function报错和接口数据拉取异常是多段逻辑问题叠加导致,和Redux Toolkit基础配置无关:

  • 初始渲染阶段空值直接调用数组方法:组件首次加载时Redux中list初始值是空数组,const [mockData, setMockData] = useState(list[0])取到的值为undefined,后续直接对mockData调用.slice()会直接抛出方法不存在的错误。
  • useState初始值不会随Redux状态同步:useState的初始参数仅在组件首次挂载时生效,后续Redux中list更新后,mockData不会自动同步最新值,代码中也没有写同步逻辑,所以始终拿不到接口返回的数据。
  • Redux存储数据时多包了一层数组:如果接口本身返回的就是列表数组,getList.fulfilled中写的state.list = [action.payload]会把整个返回数组包成二维结构,就算数据同步成功,后续分页逻辑也会和预期不符。
  • 请求逻辑缺少状态判断和异常捕获:fetch默认不会因HTTP 4xx/5xx状态码标记为请求失败,直接调用res.json()可能拿到错误响应体而非业务数据;另外Postman不受浏览器同源策略限制,如果接口没配置CORS响应头,浏览器会直接拦截跨域请求,这也是Postman能调通但页面拿不到数据的常见原因。
  • 缺少加载、错误状态兜底:代码中没有处理请求中、请求失败、数据为空的场景,直接渲染业务逻辑很容易触发报错。
修复方案

1. 修正Redux Slice逻辑

修正数据存储结构,补全请求状态判断和错误捕获,替换已废弃的extraReducers对象写法:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const getList = createAsyncThunk('list/getList', async (_id, { rejectWithValue }) => {
  try {
    const res = await fetch('http://13.xx.94.xx/neon/api/v1/List/RetrieveAll');
    if (!res.ok) throw new Error(`请求异常,状态码:${res.status}`);
    const data = await res.json();
    // 根据接口实际返回结构调整,直接返回数组则取data,返回{data:数组}结构则取data.data
    return Array.isArray(data) ? data : data.data;
  } catch (err) {
    return rejectWithValue(err.message);
  }
});

const listSlice = createSlice({
  name: 'list', 
  initialState: {
    list: [],
    loading: false,
    error: null
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getList.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(getList.fulfilled, (state, action) => {
        state.loading = false;
        // 直接存储返回的数组,不要额外套方括号
        state.list = action.payload;
      })
      .addCase(getList.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload;
      })
  }
});

export default listSlice.reducer;

2. 修正组件渲染逻辑

移除冗余的mockData本地状态,直接使用Redux存储的列表数据,补全状态兜底和缺失的状态定义:

const List = () => {
  // 优化useSelector写法,避免每次渲染生成新对象导致不必要重渲染
  const { loading, list, error } = useSelector((state) => state.list);
  const dispatch = useDispatch();
  // 补全原代码缺失的分页页码状态
  const [pageNum, setPageNum] = useState(0);
  // 原代码用到的弹窗状态如果是本地状态也需要补全定义
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    dispatch(getList());
  }, [dispatch]);

  // 状态兜底渲染
  if (loading) return <div>数据加载中...</div>;
  if (error) return <div>加载失败:{error}</div>;
  if (!Array.isArray(list) || list.length === 0) return <div>暂无数据</div>;

  const dataPerPage = 10;
  const dataPageVisited = pageNum * dataPerPage;
  // 直接对Redux中存储的列表做分页处理
  const displayData = list.slice(dataPageVisited, dataPageVisited + dataPerPage).map((datum) => (
    <tr key={datum.id}>
      <td className="text-sm leading-5 py-4 px-3">{datum.id}</td>
      <td className="py-4 uppercase text-center">{datum.name}</td>
      <td className="py-4 pr-4 pl-20">
        {datum.status === 'Active' ? (
          <span className="flex items-center bg-green-300 py-0.3 px-0.2 w-14 rounded-xl text-white">
            <GoPrimitiveDot className="text-white" />
            {datum.status}
          </span>
        ) : (
          <span className="flex items-center bg-red-400 py-0.3 px-0.2 w-16 rounded-xl text-white">
            <GoPrimitiveDot className="text-white" />
            {datum.status}
          </span>
        )}
      </td>
      <td className="py-4 pl-4">{datum.websiteUrl}</td>
      <td className="py-4 pl-10">
        <span className="inline-block text-textTeams py-0.5 px-0.4 w-16 bg-indigo-50 rounded-lg text-center hover:cursor-pointer">
          {datum.category}
        </span>
      </td>
      <td className="py-4 px-6">
        <span className="flex justify-between">
          <button type="button">
            <FiSearch className="search-icon hover:cursor-pointer w-5 h-5 text-searchColor" />
          </button>
          <RiDeleteBinLine
            className="delete-icon hover:cursor-pointer w-5 h-5 text-binColor"
            onClick={() => setIsOpen(true)}
          />
          <button type="button">
            <FiEdit2 className="pen-icon hover:cursor-pointer w-5 h-5 text-penColor" />
          </button>
        </span>
      </td>
    </tr>
  ));

  return(
    <>
      {displayData}
      {/* 补全分页切换组件,修改pageNum即可触发分页切换 */}
    </>
  )
}

3. 跨域问题处理

如果改完上述代码控制台仍报CORS相关错误:

  • 开发环境可以在Vite/Webpack等构建工具中配置接口代理,转发请求绕过浏览器同源限制
  • 生产环境需要后端给接口配置Access-Control-Allow-Origin等CORS响应头,放开前端域名的访问权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:33:22