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

React中使用useState筛选数据时出现重渲染问题

React useState筛选功能重渲染问题解决

我在App.js中尝试使用useState Hook修改HouseDetails数据以实现筛选功能,但始终出现重渲染问题。以下是App.js和Card.js的代码,即使已做相关尝试,该问题仍未解决。

App.js 原代码

import "./App.css";
import Card from "./components/Card";
import NavBar from "./components/NavBar";
import Header from "./components/Header";
import { HiChevronDown } from "react-icons/hi";
import { useState, useMemo } from "react";
import HouseDetails from "./components/RealState/HouseDetails";

function App() {
  const [items, setItems] = useState(HouseDetails);
  
  
  const filterHouse = (e) => {

    const newHouse = e.filter((f) => {
      f.name.includes('Collsmith'); return 1;
    })

    // dont call setItems inside useMemo
    // it triggers another render 
    // which triggers another useMemo call
    // setItems(newHouse);
    
  }
  
  
  const updatedItems = useMemo(() => filterHouse(items), [items]);
  setItems(updatedItems);

  return (
    <div className="w-full justify-center">
      <Header />
      <main className="w-[76%] m-auto">
        <div className="my-14 py-2">
          <div className="sm:flex justify-between items-center">
            <ul>
              <li className="hidden sm:block lg:text-4xl font-semibold">
                Search properties to rent
              </li>
            </ul>
            <ul className="flex items-center justify-between border-2 border-gray-300 px-3 py-2 rounded-md text-gray-600">
              <li className="font-semibold">
                <input
                  type="search"
                  className="outline-none"
                  placeholder="Search with Search Bar"
                />
              </li>
              <li>
                <HiChevronDown />
              </li>
            </ul>
          </div>
          <NavBar />
        </div>

        {/* main-content */}

        <div className="grid xl:grid-cols-3 lg:grid-row-3 md:grid-cols-2 lg:gap-8 md:gap-6 sm:gap-4">
          <Card items={items} />
        </div>
      </main>
    </div>
  );
}

export default App;

Card.js 原代码

import React, { useState, useMemo } from "react";
import { FiHeart } from "react-icons/fi";
import { MdBed } from "react-icons/md";
import { BiBath, BiArea } from "react-icons/bi";

const Card = ({ items }) => {

  return items && items.map((elem) => {
    const { id, image, name, location, price, beds, bathrooms, area } = elem;
    return (
      <article key={id} className="flex-col justify-center rounded-lg shadow-md max-w-xl">
        <div className="rounded-lg">
          <img src={require('./house1.jpg')} alt="" className="rounded-lg h-72 w-full" />
        </div>
        <div className="flex-col justify-center px-4 py-2 space-y-3">
          <div className="flex-col justify-center space-y-1">
            <ul className="flex justify-between items-center">
              <li className="text-purple-600 font-bold text-2xl">
                ${price}
                <span className="text-sm text-gray-400 font-semibold">
                  /month
                </span>
              </li>
              <li className="cursor-pointer p-2 rounded-full border-2 border-gray-300 text-xl text-purple-600">
                <FiHeart />
              </li>
            </ul>
            <ul>
              <li className="font-bold text-gray-600 text-2xl">{name}</li>
            </ul>
            <ul>
              <li className="text-gray-400 text-sm">{location}</li>
            </ul>
          </div>
          <div className="flex justify-between items-center py-3">
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <MdBed />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {beds} Beds
              </li>
            </ul>
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <BiBath />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {bathrooms} Bathrooms
              </li>
            </ul>
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <BiArea />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {area} m2
              </li>
            </ul>
          </div>
        </div>
      </article>
    );
  });
};

export default Card;

问题根源分析

  1. 无限重渲染循环:直接在组件顶层调用setItems(updatedItems),每次组件渲染都会触发state更新,更新后又会触发新的渲染,形成无限循环。
  2. 筛选逻辑错误:filterHouse里的filter回调没有正确返回判断结果,当前写法不管f.name.includes('Collsmith')是否为真,都会返回1(等同于true),等于没有筛选效果。
  3. useMemo误用:useMemo的作用是缓存计算结果,避免重复计算,但你用它来触发筛选然后直接更新state,完全违背了它的使用场景。

修复方案

  1. 分离原始数据与筛选结果:保留原始数据的副本,避免筛选后丢失原始数据,用单独的state存储筛选结果。
  2. 正确实现筛选函数:在筛选回调中直接返回判断条件,确保筛选逻辑生效。
  3. 在合适时机触发筛选:绑定搜索框的onChange事件,实时触发筛选,而不是在组件渲染时自动触发。
  4. 优化组件重渲染:用React.memo包裹Card组件,避免props未变化时的不必要重渲染。

修复后的代码

App.js

import "./App.css";
import Card from "./components/Card";
import NavBar from "./components/NavBar";
import Header from "./components/Header";
import { HiChevronDown } from "react-icons/hi";
import { useState, useMemo } from "react";
import HouseDetails from "./components/RealState/HouseDetails";

function App() {
  // 保存原始数据,避免筛选后丢失
  const [originalItems] = useState(HouseDetails);
  // 存储搜索关键词
  const [searchKeyword, setSearchKeyword] = useState('');

  // 用useMemo缓存筛选结果,只有当原始数据或搜索关键词变化时才重新计算
  const filteredData = useMemo(() => {
    if (!searchKeyword) return originalItems;
    return originalItems.filter(item => 
      item.name.toLowerCase().includes(searchKeyword.toLowerCase())
    );
  }, [originalItems, searchKeyword]);

  return (
    <div className="w-full justify-center">
      <Header />
      <main className="w-[76%] m-auto">
        <div className="my-14 py-2">
          <div className="sm:flex justify-between items-center">
            <ul>
              <li className="hidden sm:block lg:text-4xl font-semibold">
                Search properties to rent
              </li>
            </ul>
            <ul className="flex items-center justify-between border-2 border-gray-300 px-3 py-2 rounded-md text-gray-600">
              <li className="font-semibold">
                <input
                  type="search"
                  className="outline-none"
                  placeholder="Search with Search Bar"
                  value={searchKeyword}
                  onChange={(e) => setSearchKeyword(e.target.value)}
                />
              </li>
              <li>
                <HiChevronDown />
              </li>
            </ul>
          </div>
          <NavBar />
        </div>

        {/* main-content */}

        <div className="grid xl:grid-cols-3 lg:grid-row-3 md:grid-cols-2 lg:gap-8 md:gap-6 sm:gap-4">
          <Card items={filteredData} />
        </div>
      </main>
    </div>
  );
}

export default App;

Card.js(优化重渲染)

import React from "react";
import { FiHeart } from "react-icons/fi";
import { MdBed } from "react-icons/md";
import { BiBath, BiArea } from "react-icons/bi";

// 用React.memo包裹,避免props未变化时重渲染
const Card = React.memo(({ items }) => {
  return items?.map((elem) => {
    const { id, image, name, location, price, beds, bathrooms, area } = elem;
    return (
      <article key={id} className="flex-col justify-center rounded-lg shadow-md max-w-xl">
        <div className="rounded-lg">
          <img src={require('./house1.jpg')} alt={name} className="rounded-lg h-72 w-full" />
        </div>
        <div className="flex-col justify-center px-4 py-2 space-y-3">
          <div className="flex-col justify-center space-y-1">
            <ul className="flex justify-between items-center">
              <li className="text-purple-600 font-bold text-2xl">
                ${price}
                <span className="text-sm text-gray-400 font-semibold">
                  /month
                </span>
              </li>
              <li className="cursor-pointer p-2 rounded-full border-2 border-gray-300 text-xl text-purple-600">
                <FiHeart />
              </li>
            </ul>
            <ul>
              <li className="font-bold text-gray-600 text-2xl">{name}</li>
            </ul>
            <ul>
              <li className="text-gray-400 text-sm">{location}</li>
            </ul>
          </div>
          <div className="flex justify-between items-center py-3">
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <MdBed />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {beds} Beds
              </li>
            </ul>
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <BiBath />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {bathrooms} Bathrooms
              </li>
            </ul>
            <ul className="flex justify-between items-center">
              <li className="text-xl pr-2 text-purple-600">
                <BiArea />
              </li>
              <li className="text-xs text-gray-400 font-semibold">
                {area} m2
              </li>
            </ul>
          </div>
        </div>
      </article>
    );
  });
});

export default Card;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:10:31