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

React子组件调用父组件closeCard函数状态不更新求助

卡片关闭按钮点击后状态变更不生效的问题解决

我有一个包含多张卡片的父组件LayoutGrid,父组件与子组件代码在同一个tsx文件中。实现了两种关闭选中卡片的方式:

  • 点击父组件网格区域:正常工作
  • 点击选中卡片上的关闭按钮:控制台显示closeCard函数已调用,但界面状态未更新

LayoutGrid.tsx

export const LayoutGrid = ({
  className,
  cards
}: {
  className?: string,
  cards: LayoutCardProps[]
}) => {
  const [selected, setSelected] = useState<LayoutCardProps | null>(null);
  const [lastSelected, setLastSelected] = useState<LayoutCardProps | null>(null);
  const [blockScroll, allowScroll] = useScrollBlock();

  const handleClick = (card: LayoutCardProps) => {
    setLastSelected(selected);
    setSelected(card);
  };

  const closeCard = () => {
    setLastSelected(selected);
    setSelected(null);
    console.log("yes?")
  };

  useEffect(() => {
    if (selected) {
      handleSmoothScroll('layout-grid');
      blockScroll();
    } else {
      allowScroll();
    }
  }, [selected])

  return (<>
    {/* {selected&&
      <div className="h-[100vh] w-[100vw] fixed inset-0 bg-slate-400 z-40"/>
    } */}
    <div id="layout-grid" className={cn('w-full h-full p-2 sm:p-5 md:p-10 grid grid-cols-1 md:grid-cols-3  max-w-7xl mx-auto gap-4 relative', className)}>
      {cards.map((card, i) => (
        <div key={i} className={cn(card.className, "")}>
          <motion.div
            // id={selected?.id === card?.id ? 'selected-card' : `card-${i}`}
            onClick={() => handleClick(card)}
            className={cn(
              card.className,
              "relative scroll-offset-top-biography",
              selected?.id === card.id
                ? "rounded-lg fixed inset-0 top-10 md:top-20 h-[75vh] md:h-[85vh] w-[95%] sm:w-[85%] md:w-[80%] m-auto z-50 flex justify-center items-center flex-wrap flex-col"
                : `bg-white rounded-xl h-full w-full border ${lastSelected?.id === card.id && 'z-40'}`
            )}
            layoutId={`card-${card.id}`}
          >
            {card.thumbnail && <ImageComponent card={card} />}
            {selected?.id === card.id ?
              <SelectedCard selected={selected} closeCard={closeCard}/>
              :
              <ContentCard card={card} />
            }
          </motion.div>
        </div>
      ))}
      <motion.div
        onClick={closeCard}
        className={cn(
          "absolute h-full w-full left-0 top-0 bg-black opacity-0 z-40",
          selected ? "pointer-events-auto" : "pointer-events-none"
        )}
        animate={{ opacity: selected ? 0.3 : 0 }}
      />
    </div>
  </>);
};
const SelectedCard = ({
  selected,
  closeCard,
}: {
  selected: LayoutCardProps | null,
  closeCard: ()=>void;
}) => {
  return (<>
    <div
      className="relative h-full w-full flex flex-col justify-start rounded-lg shadow-2xl z-[70]"
    >
      <motion.div
        initial={{
          opacity: 0,
        }}
        animate={{
          opacity: 0.6,
        }}
        className="absolute inset-0 h-full w-full bg-black opacity-60 rounded-xl"
      />
      <motion.div
        layoutId={`content-${selected?.id}`}
        initial={{
          opacity: 0,
          y: 100,
        }}
        animate={{
          opacity: 1,
          y: 0,
        }}
        exit={{
          opacity: 0,
          y: 100,
        }}
        transition={{
          duration: 0.3,
          ease: "easeInOut",
        }}
        className="relative h-full w-full p-2"
      >
        {selected?.contentFull}
      </motion.div>
    <button
      onClick={closeCard}
      className="absolute top-0 right-0 p-2 mr-4 mt-3 bg-slate-700 hover:bg-orange-400 rounded-full"
    >
      <MdOutlineCloseFullscreen  size={20} className="text-slate-50 hover:text-slate-900"/>
    </button>
    </div>
  </>);
};

问题原因

核心是事件冒泡:点击关闭按钮时,事件会向上传递到外层的<motion.div>,而这个div绑定了handleClick(card)方法。这会导致closeCard刚把selected设为null,handleClick又立刻把selected重新设为当前卡片,界面自然看不到变化。

解决方案

在关闭按钮的点击事件中阻止冒泡,修改SelectedCard里的按钮代码:

<button
  onClick={(e) => {
    e.stopPropagation(); // 阻止事件冒泡到父元素
    closeCard();
  }}
  className="absolute top-0 right-0 p-2 mr-4 mt-3 bg-slate-700 hover:bg-orange-400 rounded-full"
>
  <MdOutlineCloseFullscreen  size={20} className="text-slate-50 hover:text-slate-900"/>
</button>

或者另一种方式,在SelectedCard最外层容器添加事件阻止冒泡:

<div
  className="relative h-full w-full flex flex-col justify-start rounded-lg shadow-2xl z-[70]"
  onClick={(e) => e.stopPropagation()}
>
  {/* 原有内容不变 */}
</div>

这样修改后,点击关闭按钮时事件不会传递到外层的motion.div,handleClick不会被触发,closeCard的状态变更就能正常生效,界面会同步更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:03:21