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

React实现仅展开点击卡片项而非整行的方法

问题分析

所有卡片点击后同时展开,核心原因是所有卡片共用了同一个展开状态变量,导致单个卡片的状态变更会触发所有卡片的UI更新。

解决方案

方案1:让每个卡片独立维护展开状态(推荐,简单直接)

确保每个CoctailCard组件自行管理自身的展开状态,父组件仅传递鸡尾酒数据,不共享状态。

检查父组件渲染逻辑

父组件循环渲染卡片时,需保证每个卡片有唯一key(用鸡尾酒ID),且不传递共享的展开状态:

// 父组件示例代码
{cocktailsList.map(cocktail => (
  <CoctailCard key={cocktail.id} coctails={cocktail} />
))}

你的CoctailCard代码本身已具备独立维护状态的逻辑,但需注意:

  • 必须给循环渲染的卡片添加唯一key,避免React复用组件导致状态混乱
  • 组件内部的expanded状态是每个卡片实例独有的,点击单个卡片只会改变自身状态

方案2:父组件集中管理所有卡片的展开状态(适合全局控制场景)

如果需要在父组件中统一控制卡片的展开/收起,可通过对象记录每个卡片ID对应的展开状态:

步骤1:修改父组件状态管理

import { useState } from 'react';

// 父组件
const CocktailList = () => {
  // 用对象存储每个卡片的展开状态,key为卡片ID,value为布尔值
  const [expandedCards, setExpandedCards] = useState({});

  // 切换指定卡片的展开状态
  const handleCardExpand = (cardId) => {
    setExpandedCards(prev => ({
      ...prev,
      [cardId]: !prev[cardId]
    }));
  };

  return (
    <div className="card-grid">
      {cocktailsList.map(cocktail => (
        <CoctailCard
          key={cocktail.id}
          coctails={cocktail}
          expanded={!!expandedCards[cocktail.id]}
          onExpandClick={() => handleCardExpand(cocktail.id)}
        />
      ))}
    </div>
  );
};

步骤2:更新CoctailCard组件接收props

import React, {useEffect, useState} from "react";
import {CoctailEntity, GetCoctailRecipe} from "types";
import {Card, CardActions, CardContent, CardHeader, Collapse, IconButton, Typography} from "@mui/material";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';

interface Props {
    coctails: CoctailEntity;
    expanded: boolean;
    onExpandClick: () => void;
}


export const CoctailCard = (props: Props) => {

    const [ingredients, setIngradients] = useState<GetCoctailRecipe[] | null>(null)

    const cocktailIngredients = async () => {
        const res = await fetch(`http://localhost:3001/cocktail-recipe/${props.coctails.id}`, {
            headers: {}
        })
        const data = await res.json()
        setIngradients(data)
    }

    // 添加ID依赖,确保卡片切换时重新获取配料
    useEffect(() => {
        cocktailIngredients()
    }, [props.coctails.id])


    if (!ingredients) {
        return (
            <div className="coctailCard">
                <p>{props.coctails.name}</p>
                <p>Brak składników tego koktajlu</p>
            </div>
        )
    }

    return (
        <Card
            className='coctailCard'
            sx={{
                backgroundColor: 'antiquewhite',
                borderRadius: 5,

            }}
        >
            <CardHeader
                title={props.coctails.name}
                sx={{padding: '10px 0 0 10px'}}
            />
            <CardActions
                disableSpacing
                sx={{padding: '10px 0 0 10px'}}
            >
                <IconButton
                    sx={[
                        {
                            transform: 'rotate(0deg)',
                            transition: (theme) => theme.transitions.create('all', {
                                duration: theme.transitions.duration.shortest,
                            })
                        },
                        props.expanded && {
                            transform: 'rotate(180deg)'
                        },
                    ]}
                    onClick={props.onExpandClick}
                    aria-expanded={props.expanded}
                    aria-label="show more"
                >
                    <ExpandMoreIcon/>
                </IconButton>
            </CardActions>
            <Collapse in={props.expanded} timeout="auto" unmountOnExit>
                <CardContent>
                    <Typography paragraph>Skład:</Typography>
                    <Typography paragraph>
                        {ingredients.map(ingradient => <p className='ingradientRow'>{`${ingradient.name} ${ingradient.quantity}ml`}</p>)}
                    </Typography>
                    <Typography paragraph>
                        {props.coctails.description}
                    </Typography>
                </CardContent>
            </Collapse>
        </Card>
    )
}
额外优化建议
  • 给useEffect添加props.coctails.id依赖,确保卡片ID变化时重新获取对应配料数据
  • 循环渲染卡片时必须添加唯一key属性,避免React组件复用导致状态异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 08:15:01