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

React点击按钮仅能显示一次组件的问题排查与修复

修复React中Invoice组件仅能显示一次的问题

问题根源分析

  1. Props接收错误:原Invoice组件未解构props,导致item.item._id的嵌套引用逻辑混乱,可能引发数据获取失败。
  2. 显示依赖错误:组件显示逻辑依赖bill._id,关闭弹窗时执行setBill([])清空数据,后续即使触发重新渲染,也因bill._id不存在无法显示。
  3. Effect触发条件不足:useEffect仅监听item变化,关闭弹窗后item未改变时,无法重新触发数据获取。
  4. 未定义变量错误:useEffect中使用了未声明的open变量,导致状态切换逻辑失效。

修复方案:状态提升+明确显示控制

将数据获取和显示控制逻辑提升到父组件,子组件专注于渲染,符合React单向数据流原则,彻底解决重复触发问题。

父组件修改

import { useState } from 'react';

// 父组件中管理账单数据和显示状态
const [showInvoice, setShowInvoice] = useState(false);
const [currentBill, setCurrentBill] = useState(null);

// 点击按钮时获取账单数据并打开弹窗
const getBillDetails = async (item) => {
  const response = await allBills(item._id);
  setCurrentBill(response.data);
  setShowInvoice(true);
};

// 渲染部分
<button onClick={() => getBillDetails(item)}>show</button>
<Invoice 
  bill={currentBill} 
  isVisible={showInvoice} 
  onClose={() => setShowInvoice(false)} 
/>

Invoice子组件修改

import ReactDOM from 'react-dom';
import { useContext } from 'react';
import { ThemeContext } from './path-to-theme-context'; // 替换为实际路径

const Invoice = ({ bill, isVisible, onClose }) => {
  const Theme = useContext(ThemeContext);

  // 不满足显示条件时直接返回null
  if (!isVisible || !bill?._id) return null;

  return ReactDOM.createPortal(
    <div id="form__add">
      {/* 点击遮罩关闭弹窗 */}
      <div className="backdrop" onClick={onClose}></div>
      
      <div className="invoice__header">
        <div className="invoice__header__left">
          <h1>Invoice</h1>
          <p>Invoice No: {bill._id}</p>
          <p>Invoice Date: {bill.createdAt}</p>
          <p>Payment Method: {bill.paymentMethod}</p>
          <p>
            <span>Customer Name: {bill.customer?.name}</span>
            <span>Customer Phone: {bill.customer?.phone}</span>
          </p>
          
          <table>
            <thead>
              <tr>
                <th>Product</th>
                <th>Quantity</th>
                <th>Price</th>
                <th>Total</th>
              </tr>
            </thead>
            <tbody>
              {bill.products?.map((product, index) => (
                <tr key={index}>
                  <td>{product.name}</td>
                  <td>{product.quantity}</td>
                  <td>{product.price}</td>
                  <td>{product.quantity * product.price}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>,
    document.getElementById('modal')
  );
};

export default Invoice;

修复关键点

  • 状态提升:父组件统一管理数据获取和显示状态,避免子组件内部状态混乱。
  • 明确显示控制:通过isVisible prop控制弹窗显示,不再依赖数据是否存在,确保每次点击都能触发显示。
  • 健壮性优化:使用可选链?.避免空值访问报错,减少潜在bug。
  • 简化逻辑:关闭弹窗仅切换显示状态,无需清空数据,再次点击时重新获取最新数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 21:22:02