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

React-Redux中API数据过滤变量无法初始化填充的问题排查

问题

我用React+Redux从API获取数据,创建了filterClassifieds变量用来处理数据并展示结果,但页面加载时这个变量始终为空,没法从原始数据源classifieds填充数据,导致表格显示0条结果。请问要在React组件还是Redux的actions/reducers里修改实现?

补充说明:初始化dispatch前classifieds和filterClassifieds都为空,但数毫秒后classifieds已填充数据,filterClassifieds仍然没有数据。


相关代码

classifieds.js

import React, { useState,useEffect,useMemo } from "react";
import './classifieds.scss'
import { OverlayBox } from "../../components/Main-Pages/overlay-box/overlay-box";
import { SideFilter } from "../../components/Main-Pages/side-filter/side-filtrer";
import Table from 'react-bootstrap/Table';
import { NavLink } from 'react-router-dom';
import Card from 'react-bootstrap/Card';
import { useDispatch, useSelector } from 'react-redux';
import SpinnerButton from "../../components/Spinner/Spinner";
import { getClassifieds } from "../../redux/actions/classifiedsAction";

export const Classifieds = ()=>{

  const dispatch = useDispatch();

    const buttons = ['Show All','Rental','Services','Wanted','Cars','For Sale', 'More'];
    const moreButtons = ['Baby sitting','Beauty parlor','Fashion','Household Help'
    ,'Real Estate','Others'];

    const [active,setActive] = useState('Show All');
    const [activeMoreButtons,setActiveMoreButtons] = useState('');
    const [modal,setModal] = useState(false);

  const classifieds = useSelector((state) => state.classifieds.Classifieds);
  const classifiedsLoading = useSelector((state) => state.classifieds.loading);

  // const [filterClassifieds,setFilterClassifieds] = useState(classifieds);

  // useEffect(() => {
  //   setFilterClassifieds(classifieds);
  // }, [classifieds]);

  const filterClassifieds = classifieds.filter((item) => {
    return active !== "Show All" ? item.category === active : true;
  });
  
    const handleButton = (name)=>{
        if(modal)setModal(false);
        setActiveMoreButtons('');
        setActive(name);
        // if(name==='Show All'){
        //    setFilterClassifieds(classifieds);
        // }else{
        //    let newClassifieds = classifieds.filter((itm)=>
        //   {
        //     return itm.category === name;
        //   }
        //   )
        //   setFilterClassifieds(newClassifieds);
        // }
    }

    const openModal =()=>{
        setModal(!modal);
    }

    const handleMoreButton=(name)=>{
        setActive('More');
        setModal(!modal);
        setActiveMoreButtons(name)
    }

    useEffect(() => {
      const generateClassifieds = async () => {
          await dispatch(getClassifieds());
        };
        generateClassifieds();
  }, [dispatch]);


  const transformDate = (datePosted)=> {
      let newDate = new Date(datePosted);
      return newDate.toLocaleDateString("en-US");
  }

  // console.log(filterClassifieds);
    return(
        <div>
         <OverlayBox name={"Classifieds"}/>
        <div className="section properties">
    <div className="container">
      <ul className="properties-filter">
          {
            buttons.map((name,index)=>{
            return name!=='More' ?(
            <li key={ index }>
            <button
                 className={active === name ? 'is_active' : ''}
                 onClick={()=>handleButton(name)}
                 >{name}</button>        
            </li>
            ):( 
                <li key={ index }>
            <button
                 className={active === name ? 'is_active' : ''}
                 onClick={()=>openModal(name)}
                 >
                    {name}
                 <i className="fa fa-arrow-down-short-wide"></i>
                 </button>
                 {
                    modal?
                        <Card body className='card-design'>
                            <ul>
                            {moreButtons.map((val,index)=>(
                            <li className={activeMoreButtons === val ? 'card-active card-li' : 'card-li'} key={index} onClick={()=>handleMoreButton(val)}>        
                            {val}
                            </li>
                            ))}
                            </ul>
                            </Card>:<></>
                 }        
            </li>
            )
})
          }
      </ul>
      <div className="row properties-box">
        <div className="col-9 properties-header">
            <p className="properties-text">
                Free Classifieds
            </p>
            </div>
        <div className="col-3 text-end properties-text">
        <NavLink to='/post-ad' className='col-lg-2'>Post Ad</NavLink>
        </div>
      <Table striped hover>
      <thead>
        <tr>
          <th>Category</th>
          <th>Description</th>
          <th>Date Posted</th>
        </tr>
      </thead>
        {classifiedsLoading?<></>:
      <tbody>
        {filterClassifieds?.map((val,ind)=>(
          <tr key={ind}>
            <td>{val?.category}</td>
            <td>{val?.heading.split('', 86).reduce((o, c) => o.length === 85 ? `${o}${c}...` : `${o}${c}` , '')}</td>
            <td>{transformDate(val?.date)}</td>
          </tr>
        ))}
      </tbody>
        }
    </Table>
      </div>
    </div>
  </div>
            </div>
    )
}

action.js

import axios from "axios";

export const GET_CLASSIFIEDS = "GET_CLASSIFIEDS";
export const CLASSIFIEDS_LOADING = "CLASSIFIEDS_LOADING";

// Get all posts
export const getClassifieds = () => (dispatch) => {
    dispatch(setClassifiedsLoading());
    axios
      .get("/posts")
      .then((res) =>
        dispatch({
          type: GET_CLASSIFIEDS,
          payload: res.data,
        })
      )
      .catch((err) =>
        dispatch({
          type: CLASSIFIEDS_LOADING,
          payload: {},
        })
      );
  };
  
  // Classifieds loading
  export const setClassifiedsLoading = () => {
    return {
      type: CLASSIFIEDS_LOADING,
    };
  };

reducer.js

import { GET_CLASSIFIEDS, CLASSIFIEDS_LOADING } from "../actions/classifiedsAction";

const initialState = {
  Classifieds: null,
  filterClassifieds: null,
  loading: true,
};

export const ClassifiedsReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_CLASSIFIEDS:
      return {
        ...state,
        Classifieds: action.payload,
        loading: false,
      };
    case CLASSIFIEDS_LOADING:
      return {
        ...state,
        loading: true,
      };
    default:
      return state;
  }
};

原因分析

问题出在filterClassifieds的定义逻辑:

  1. 初始状态下Redux中的classifieds是null,直接调用classifieds.filter()会触发隐性错误,导致filterClassifieds无法正常生成。
  2. 后续classifieds获取到数据后,组件重新渲染时虽然会重新执行过滤代码,但之前的错误逻辑已经影响了变量的正常赋值。
  3. 过滤属于组件UI状态相关的派生逻辑,完全不需要放到Redux的actions/reducers中处理。

解决方案

在React组件中修改filterClassifieds的定义,用useMemo优化性能并处理classifieds为空的情况:

// 替换原来的filterClassifieds定义
const filterClassifieds = useMemo(() => {
  if (!classifieds) return [];
  return classifieds.filter((item) => {
    return active !== "Show All" ? item.category === active : true;
  });
}, [classifieds, active]);

说明

  1. 先判断classifieds是否存在,为空时返回空数组,避免调用filter方法报错。
  2. 用useMemo缓存过滤结果,只有当classifieds或active状态变化时才重新执行过滤,减少不必要的渲染开销。
  3. 当API返回数据后,classifieds更新,useMemo会自动重新计算过滤结果,表格就能正常渲染数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 08:52:03