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

React中useEffect与useState结合Filter使用的问题(疑似渲染循环)

Fixing React State Update Issues After Moving Async Requests to a Service File

Hey there! Let's break down exactly what's going wrong here and walk through how to fix it.

The Root of the Problem

Your getProducts() function is asynchronous — it returns a Promise, not the actual product array you expect. When you call setProducts(getProducts()) directly in useEffect, you're setting your products state to a Promise object instead of the data from your backend. Later, when you try to run filter on that Promise (which isn't an array), you get the "Cannot filter undefined" error. Plus, if your dependency array isn't set correctly, this can trigger unnecessary re-renders and even render loops.

Step-by-Step Solution

1. Ensure Your Service Function is Correctly Written

First, double-check your Product.js service file to make sure it properly returns the fetched data as a Promise:

// services/Product.js
export const getProducts = async () => {
  try {
    // Replace with your actual backend endpoint
    const response = await fetch('/api/products');
    
    if (!response.ok) {
      throw new Error('Failed to fetch products');
    }
    
    return await response.json();
  } catch (error) {
    console.error('Fetch error:', error);
    throw error; // Let the component handle the error
  }
};

2. Update Your Component to Handle Async Logic Properly

In your letter.js component, you need to wait for the Promise to resolve before updating state. You also want to handle loading/error states to avoid rendering issues:

// views/letter.js
import { useState, useEffect } from 'react';
import { getProducts } from '../../services/Product';

const LetterView = () => {
  // Initialize products as an empty array to avoid filter errors upfront
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Wrap async logic in an inner function (useEffect can't be async directly)
    const fetchAndSetProducts = async () => {
      try {
        setIsLoading(true);
        // Wait for the async function to resolve and get the actual data
        const fetchedProducts = await getProducts();
        setProducts(fetchedProducts);
      } catch (err) {
        setError(err.message);
      } finally {
        setIsLoading(false);
      }
    };

    fetchAndSetProducts();
  }, []); // Empty dependency array = run only once on component mount

  // Handle loading/error states before rendering the product list
  if (isLoading) return <div>Loading products...</div>;
  if (error) return <div>Oops: {error}</div>;

  return (
    <div className="product-list">
      {/* Now filter is safe to use since products is always an array */}
      {products.filter(product => product.type === 'letter').map(item => (
        <div key={item.id} className="product-item">
          {item.name}
        </div>
      ))}
    </div>
  );
};

export default LetterView;

Key Fixes to Note

  • Initialize state with an empty array: This ensures products.filter never runs on undefined while waiting for the API response.
  • Wait for the Promise to resolve: Using await on getProducts() ensures you only pass actual data to setProducts.
  • Avoid async useEffect callbacks: React warns against async functions directly in useEffect, so we wrap the logic in an inner async function.
  • Add loading/error states: This improves user experience and prevents rendering errors during data fetching.
  • Set proper dependencies: The empty dependency array ensures the fetch only runs once on mount (no render loops). If you need to refetch when a value changes (e.g., a category filter), add that value to the dependency array.

内容的提问来源于stack exchange,提问作者SERGIO GARCÍA GONZÁLEZ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:07:27