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

React跨组件重置:点击Right组件按钮清空Left组件输入框

React组件重置状态解决方案

问题分析

当前代码存在三个核心问题导致重置功能失效:

  1. Right组件的Reset按钮未绑定点击事件,无法触发状态重置
  2. Left组件的NumericFormat输入框未绑定value属性,状态更新后输入框内容不会同步变化
  3. 状态初始值类型与重置目标(空字符串)不匹配,且部分逻辑判断存在类型错误

解决步骤

1. 父组件添加重置函数并传递给Right组件

修改App.js,将状态初始值调整为符合输入框空值的类型,新增重置函数并传递给Right组件:

import { useState } from "react";
import Left from "./Left";
import Right from "./Right";
import "./App.scss";

export default function App() {
  // 初始状态改为空字符串,匹配输入框空值需求
  const [billAmount, setBillAmount] = useState("");
  const [personAmount, setPersonAmount] = useState("");
  const [selectedTip, setSelectedTip] = useState(0);
  const [perCost, setPerCost] = useState(0);

  // 重置所有相关状态
  const handleReset = () => {
    setBillAmount("");
    setPersonAmount("");
    setSelectedTip(0);
    setPerCost(0);
  };

  return (
    <div id="content" className="main__cont">
      <Left
        billAmount={billAmount}
        setBillAmount={setBillAmount}
        personAmount={personAmount}
        setPersonAmount={setPersonAmount}
        selectedTip={selectedTip}        
        setSelectedTip={setSelectedTip}
        perCost={perCost}
        setPerCost={setPerCost}
      />

      <Right
        billAmount={billAmount}
        setBillAmount={setBillAmount}
        personAmount={personAmount}
        setPersonAmount={setPersonAmount}
        selectedTip={selectedTip}
        setSelectedTip={setSelectedTip}
        perCost={perCost}
        setPerCost={setPerCost}
        handleReset={handleReset} // 传递重置函数
      />
    </div>
  );
}

2. 给Right组件的Reset按钮绑定点击事件

修改Right.js,添加按钮点击事件并更新PropTypes:

import PropTypes from "prop-types";
import "./right.scss";

Right.propTypes = {
  billAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  setBillAmount: PropTypes.func.isRequired,
  perCost: PropTypes.number.isRequired,
  setPerCost: PropTypes.func.isRequired,
  personAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  setPersonAmount: PropTypes.func.isRequired,
  selectedTip: PropTypes.number.isRequired,  
  setSelectedTip: PropTypes.func.isRequired,
  handleReset: PropTypes.func.isRequired, // 添加重置函数类型校验
};

export default function Right(props) {
  // 调整禁用判断逻辑,适配空字符串状态
  const isResetDisabled = !props.billAmount && !props.personAmount;

  let tipAmount = props.perCost - (props.billAmount / props.personAmount || 0);
  tipAmount = isNaN(tipAmount) || !isFinite(tipAmount) || tipAmount < 0 ? 0 : tipAmount;

  let perTotal = (props.billAmount / props.personAmount || 0) + tipAmount;
  perTotal = isNaN(perTotal) || !isFinite(perTotal) || perTotal < 0 ? 0 : perTotal;

  return (
    <div className="right">
      <div className="right__group">
        <div className="leftmain__cont">
          <div className="right__text">
            <p className="right__label">Tip Amount</p>
            <p className="right__textsmall">/ person</p>
          </div>
          <h3 className="right__inbtn">${tipAmount.toFixed(2)}</h3>
        </div>
        <div className="rightmain__cont">
          <div className="right__text">
            <p className="right__label">Total</p>
            <p className="right__textsmall">/ person</p>
          </div>
          <h3 className="right__inbtn">${perTotal.toFixed(2)}</h3>
        </div>
      </div>

      {/* 绑定重置点击事件 */}
      <button 
        className="right__btn" 
        disabled={isResetDisabled}
        onClick={props.handleReset}
      >
        Reset
      </button>
    </div>
  );
}

3. 给Left组件的输入框绑定value属性,同步状态变化

修改Left.js,更新TipsInput组件的调用和定义,确保输入框与状态同步:

import PropTypes from "prop-types";
import { NumericFormat } from "react-number-format";
import "./left.scss";

Left.propTypes = {
  billAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  setBillAmount: PropTypes.func,
  perCost: PropTypes.number,
  setPerCost: PropTypes.func,
  personAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  setPersonAmount: PropTypes.func,
  selectedTip: PropTypes.number,
  setSelectedTip: PropTypes.func,
};

const tips = [5, 10, 15, 20, 25, 50];

// 调整TipsInput,接收value属性并传递给NumericFormat
const TipsInput = ({ label, id, handleChange, name, placeholder, value }) => {
  const handleInputChange = (values) => {
    const { value: inputValue } = values;
    const numericValue = parseFloat(
      inputValue.replace(label === "Bill" ? "$" : "", "")
    );
    handleChange(isNaN(numericValue) ? "" : numericValue.toString());
  };

  return (
    <>
      <label htmlFor={id}>{label}</label>
      <NumericFormat
        className="left__input"
        type="text"
        id={id}
        name={name || id}
        placeholder={placeholder}
        allowLeadingZeros={false}
        thousandSeparator=","
        prefix={label === "Bill" ? "$" : ""}
        onValueChange={handleInputChange}
        value={value} // 绑定value,同步状态
      />
    </>
  );
};

TipsInput.propTypes = {
  label: PropTypes.string.isRequired,
  id: PropTypes.string.isRequired,
  handleChange: PropTypes.func.isRequired,
  name: PropTypes.string,
  placeholder: PropTypes.string,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
};

export default function Left({
  billAmount,
  setBillAmount,
  perCost,
  setPerCost,
  personAmount,
  setPersonAmount,
  selectedTip,
  setSelectedTip,
}) {
  const isBillEmpty = billAmount === "";

  const handleTipClick = (percentage) => {
    setSelectedTip(percentage / 100);
  };

  const calculatePerPeronAmount = (tip) => {
    const billNum = parseFloat(billAmount);
    const personNum = parseFloat(personAmount);
    if (personNum > 0 && billNum) {
      setPerCost(
        ((1 + 0.01 * tip) * billNum) / personNum
      );
    } else {
      setPerCost(0);
    }
  };

  return (
    <div className="left">
      <div className="left__group">
        {/* 传递value给TipsInput */}
        <TipsInput 
          handleChange={setBillAmount} 
          label="Bill" 
          placeholder="$" 
          value={billAmount}
        />
      </div>
      <div className="left__group">
        <label htmlFor="left__text">Select Tip %</label>
        <div className="tip">
          {tips.map((tip) => {
            return (
              <button
                className={`tip__btn ${isBillEmpty ? "disabled" : ""}`}
                disabled={isBillEmpty}
                onClick={() => {
                  calculatePerPeronAmount(tip);
                  handleTipClick(tip); // 修复:传递当前点击的tip值
                }}
                key={tip}>
                {tip}%
              </button>
            );
          })}
        </div>
      </div>
      <div className="left__group">
        {/* 传递value给TipsInput */}
        <TipsInput
          handleChange={setPersonAmount}
          label="Number of People"
          placeholder="People"
          value={personAmount}
        />
      </div>
    </div>
  );
}

关键修复点说明

  • 状态同步:给NumericFormat添加value属性,确保输入框内容与父组件状态实时同步
  • 重置逻辑:父组件统一管理重置函数,避免子组件重复逻辑,同时保证所有状态被正确重置
  • 类型统一:调整状态初始值为字符串,匹配输入框空值需求,同时在计算逻辑中转换为数值类型,避免NaN错误
  • 按钮事件修复:修正Tip按钮点击时传递的参数错误,确保选中的Tip值正确更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:00:55