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

React中如何在多个父组件中复用同一个CloseButton子组件

React跨父组件复用CloseButton的实现方案

现有代码问题

你当前写的CloseButton没法在两个父组件里正常复用,核心原因有三点:

  • 组件内部直接通过document.querySelector('.container_blur')查找DOM节点操作显隐,两个父组件的根节点都用了container_blur类名,两个组件同时渲染时会选错操作目标,且直接操作DOM不符合React状态驱动的开发逻辑
  • CloseButton的onClick属性为空,内部定义的close函数根本没有绑定到点击事件上,点击无响应
  • 两个父组件都没有维护自身的显隐状态,关闭逻辑完全耦合在子组件的DOM操作里,拆分复用会互相干扰

改造方案

核心思路是把CloseButton改成无状态的纯UI组件,关闭逻辑交给父组件自行控制,同一个组件可以在任意父组件中传入对应回调即可复用,不会产生冲突。

1. 重构CloseButton组件

移除内部的DOM操作逻辑,只保留UI渲染和事件触发能力,关闭方法通过props从父组件接收:

// Close_Button.js
export default function Close_Button({ onClose }) {
  return (
    <div onClick={onClose} className='d-flex close_button'>
      <svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
        <circle cx="12.5" cy="12.5" r="12" stroke="#896DF6" />
        <path d="M17 8L8 17M17 17L8 8" stroke="#896DF6" stroke-width="3" stroke-linecap="round" />
      </svg>
    </div>
  )
}

原有的淡出过渡效果不用setTimeout写在内联样式里,直接通过CSS类切换实现,维护成本更低:

/* 全局样式中添加 */
.container_blur {
  opacity: 1;
  transition: opacity 0.3s ease-out;
}
.container_blur.fade-out {
  opacity: 0;
  pointer-events: none;
}

2. 改造父组件

每个父组件单独维护自身的显隐状态,实现自己的关闭逻辑,把关闭方法传给内部的CloseButton即可,两个父组件可以完全复用同一个CloseButton组件:

// ParentOne.js
import { useState } from 'react';
import Close_Button from './Close_Button'; // 替换为你的组件实际路径
import style from './style.module.css'; // 替换为你的样式文件实际路径
import Image from 'next/image'; // 若使用普通img标签可移除该引入
import Input_search from './Input_search'; // 替换为你的组件实际路径

export default function ParentOne() {
  const [isVisible, setIsVisible] = useState(true);
  const [list, setList] = useState([
    { id: 1, name:'Bitcoin', image:'/bitcoin-logo.svg' }, 
    { id: 2, name:'Etherium', image:'/eth-logo.svg' }, 
    { id: 3, name:'Dogecoin', image:'/doge-logo.svg' },
    { id: 4, name:'Bitcoin', image:'/bitcoin-logo.svg' }, 
    { id: 5, name:'Etherium', image:'/eth-logo.svg' }, 
    { id: 6, name:'Dogecoin', image:'/doge-logo.svg' },
    { id: 7, name:'Bitcoin', image:'/bitcoin-logo.svg' }, 
    { id: 8, name:'Etherium', image:'/eth-logo.svg' }, 
    { id: 9, name:'Dogecoin', image:'/doge-logo.svg' }
  ])

  const handleClose = () => {
    const container = document.getElementById('select_token');
    container.classList.add('fade-out');
    setTimeout(() => {
      setIsVisible(false);
      container.classList.remove('fade-out');
    }, 300)
  }

  if (!isVisible) return null;

  return (
    <section className='container_blur' id='select_token' >
      <div className={style.token_container}>
        <div className={style.nav}>
          <span>Select token</span>
          <Close_Button onClose={handleClose} />
        </div>
        <Input_search />
        <div className={style.coin_container}>
          <ul className={style.ul}>
            <div className={style.li}>
              {list.map((item) =>
                <li key={item.id}>
                  <Image src={item.image} width={27} height={35} alt={item.name} />
                  <p>{item.name}</p>
                </li>
              )}
            </div>
          </ul>
        </div>
      </div>
    </section>
  )
}

第二个父组件改造逻辑完全一致:

// ParentTwo.js
import { useState } from 'react';
import Close_Button from './Close_Button';
import style from './style.module.css';
import Image from 'next/image';

export default function ParentTwo() {
  const [isVisible, setIsVisible] = useState(true);
  const [token, setToken] = useState([
    {id:1, name:'MetaMask', image:'/metaMask_Fox.png'},
    {id:2, name:'WalletConnect', image:'/wallet-connect.png'},
    {id:3, name:'KeyStone', image:'/keystone.svg'},
    {id:4, name:'Torus', image:'/torus.svg'},
    {id:5, name:'CoinBase Wallet', image:'/coinbase-wallet.png'},
    {id:6, name:'Fortmatic', image:'/fortmatic.svg'}
  ])

  const handleClose = () => {
    const container = document.getElementById('connect_w');
    container.classList.add('fade-out');
    setTimeout(() => {
      setIsVisible(false);
      container.classList.remove('fade-out');
    }, 300)
  }

  if (!isVisible) return null;

  return (
    <section className='container_blur' id='connect_w'>
      <div className={style.container_wallet}>
        <div className={style.nav}>
          <span>Connect wallet</span>
          <Close_Button onClose={handleClose} />
        </div>
        <div className={style.wallet_wrap}>
          <div className='d-flex flex-wrap align-items-stretch justify-content-between'>
            { token.map((item, id) =>
              <div key={id} className={style.wallet_select}>
                <p>{item.name}</p>
                <Image src={item.image} width={32} height={32} alt={item.name} />
              </div> 
            )}
          </div>
        </div>
      </div>
    </section>
  )
}

注意事项

  • 原代码中list.map的遍历参数和外层数组变量同名,容易引发变量污染,已统一改为item
  • ParentOne原列表数据存在id重复问题,会导致React渲染key异常,已修正为唯一id
  • 如果两个父组件的关闭逻辑完全一致,可以进一步把关闭过渡逻辑抽为自定义hook,减少重复代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:57:16