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

React同一函数内可切换多个useState吗?附ReadMore功能bug排查

问题根因

三个直接导致异常的核心问题:

  • 核心错误:ReadMore组件被定义在UsersMap父组件内部。每次父组件因状态更新触发重渲染时,ReadMore的组件引用会被重新创建,React会判定这是全新组件,直接卸载旧实例、挂载新实例,导致组件内部的isReadMore永远被重置为初始值true,无法正常切换。
  • 状态设计缺陷:isExpanded是定义在父组件的全局单一状态,所有列表卡片共用这一个状态,点击任意卡片的「read more」会同步影响所有卡片的样式,无法实现单卡独立展开/收起。
  • CSS样式问题:.users和.expand两个类都写死了固定height:25%,没有给展开态设置自适应高度规则,容器高度被锁死时自然会出现文本溢出。
修复方案

按以下步骤调整即可解决所有异常:

  1. 将ReadMore组件挪到UsersMap组件外部定义,避免父组件重渲染导致子组件被强制重置。
  2. 取消全局单一的isExpanded状态,用卡片id做key存储每张卡的独立展开状态,避免多卡状态联动。
  3. 调整CSS规则:收起态给固定高度+溢出隐藏,展开态移除固定高度,让容器随内容自适应撑开。
修复后可直接运行的代码

JSX部分

import React from 'react';
import useFetch from '../useFetch.js';
import {RestaurantBox} from '../styles/bestratedrestaurants.js'
import {Image,NameBox, TopBox, P, P2,BottomBox}  from "../styles/reviewsmap.js"
import TestPic from "../assets/london-restaurant.jpeg";
import {Bio,Box,Span} from "../styles/usersMap.js"
import {useState} from 'react';
import '../styles/usermap.css'

// ReadMore挪到父组件外部定义,避免重渲染时组件被重建
const ReadMore = ({ children, onToggle }) => {
    const text = children;
    const [isReadMore, setIsReadMore] = useState(true);
    const toggleReadMore = () => {
        setIsReadMore(!isReadMore);
        // 通知父组件切换当前卡片的展开类名
        onToggle(!isReadMore);
    };
    return (
        <p className="text">
        {isReadMore ? text.slice(0, 150) : text}
        <Span onClick={toggleReadMore}>
            {isReadMore ? "...read more" : " show less"}
        </Span>
    </p>
    );
};

const UsersMap = () => {
    const {data,isPending, error} = useFetch('http://127.0.0.1:8000/backend/api/users/list/');
    // 用对象存储每个卡片的独立展开状态,key为卡片id
    const [expandedMap, setExpandedMap] = useState({});

    // 切换单张卡片的展开状态
    const toggleCardExpand = (cardId, isExpanded) => {
        setExpandedMap(prev => ({
            ...prev,
            [cardId]: isExpanded
        }));
    };

    return ( 
        <RestaurantBox>
        {error && <div>{error}</div>}
        {isPending && <div>Loading...</div>}
        {data && data.map(item => (
            <div 
                className={ expandedMap[item.id] ? 'expand': 'users' }
                key={item.id}
            >
                <TopBox>
                    <Image src={TestPic}></Image>
                    <NameBox>
                        <P>{item.first_name} {item.last_name[0]}.</P>
                        <P2>6 Reviews in total</P2>
                    </NameBox>
                </TopBox>
                <BottomBox>
                    <Bio>
                        <ReadMore onToggle={(isExpanded) => toggleCardExpand(item.id, isExpanded)}>
                            {item.description}
                        </ReadMore>
                    </Bio>
                </BottomBox>
            </div>
        ))}
    </RestaurantBox>
    );
}

export default UsersMap

CSS部分

.users {
    margin-top:20px;
    width:18%;
    /* 收起态固定高度 */
    height:300px;
    min-width:300px;
    border-radius:3px;
    background-color: #FFFFFF;
    border-top:5px solid #E47D31;
    margin-bottom:25px;
    /* 收起态隐藏溢出内容 */
    overflow: hidden;
}

.expand{
    margin-top:20px;
    width:18%;
    /* 展开态不设固定高度,随内容自适应 */
    min-height:300px;
    min-width:300px;
    border-radius:3px;
    background-color: #FFFFFF;
    border-top:5px solid #E47D31;
    margin-bottom:25px;
    /* 展开态允许内容完全展示 */
    overflow: visible;
}
修复效果说明
  • 挪出ReadMore组件后,组件内部的isReadMore状态不会再因为父组件重渲染被重置,点击切换时可以正常在true/false间切换,文本截断/完整展示逻辑恢复正常。
  • 用expandedMap存储每张卡片的独立展开状态后,点击单张卡片的按钮只会修改当前卡片的类名,不会影响其他卡片。
  • CSS调整后,收起态固定高度隐藏溢出,展开态自适应高度,不会再出现文本溢出容器的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:12:22