TypeScript+React中如何计算多个输入框的数值总和?
Hey there! I totally get wanting to move away from the ID-based approach for calculating your form's total duration—it's not the most idiomatic way to handle state in React. Let's walk through a few cleaner, more React/TypeScript-friendly methods, along with proper ES6/TS syntax.
1. Controlled Components with an Array State (Most Recommended)
Instead of managing 8 separate state variables or querying elements by ID, store all input values in a single array state. This keeps your code DRY and easy to scale if you ever add more inputs.
Example Code:
import { useState } from 'react'; const DurationForm = () => { // Initialize state with an array of 8 zeros (one for each input) const [inputValues, setInputValues] = useState<number[]>(new Array(8).fill(0)); // Handle input change by updating the corresponding index in the array const handleInputChange = (index: number, e: React.ChangeEvent<HTMLInputElement>) => { // Parse the input value to number, default to 0 if invalid const value = parseFloat(e.target.value) || 0; // Create a copy of the array to avoid mutating state directly const newValues = [...inputValues]; newValues[index] = value; setInputValues(newValues); }; // Calculate total sum using Array.reduce (clean ES6 syntax) const totalDuration = inputValues.reduce((acc, curr) => acc + curr, 0); return ( <div> {/* Render 8 input fields dynamically */} {inputValues.map((value, index) => ( <input key={index} type="number" value={value} onChange={(e) => handleInputChange(index, e)} min="0" step="0.5" // Adjust step based on your needs (e.g., half-hour increments) /> ))} <p>Total Duration: {totalDuration}</p> </div> ); }; export default DurationForm;
Key ES6/TS Notes:
- We use
useState<number[]>to explicitly type our state array as numbers—critical for TypeScript type safety. - The spread operator (
[...inputValues]) creates a shallow copy of the array—never mutate state directly in React! parseFloat(e.target.value) || 0handles empty inputs or non-numeric values, ensuring we don't getNaNin our sum.Array.reduceis a concise ES6 method to compute the sum of an array without manual looping.
2. Controlled Components with Individual State Variables (For Small Fixed Counts)
If you prefer explicit state variables (since you have exactly 8 inputs), you can manage each one separately. This is less scalable but might feel more straightforward for a fixed number of fields.
Example Code:
import { useState } from 'react'; const DurationForm = () => { const [val1, setVal1] = useState<number>(0); const [val2, setVal2] = useState<number>(0); const [val3, setVal3] = useState<number>(0); const [val4, setVal4] = useState<number>(0); const [val5, setVal5] = useState<number>(0); const [val6, setVal6] = useState<number>(0); const [val7, setVal7] = useState<number>(0); const [val8, setVal8] = useState<number>(0); // Helper function to avoid repeating change handler logic const handleNumberChange = (setter: React.Dispatch<React.SetStateAction<number>>) => { return (e: React.ChangeEvent<HTMLInputElement>) => { setter(parseFloat(e.target.value) || 0); }; }; // Calculate total by adding all state variables const totalDuration = val1 + val2 + val3 + val4 + val5 + val6 + val7 + val8; return ( <div> <input type="number" value={val1} onChange={handleNumberChange(setVal1)} /> <input type="number" value={val2} onChange={handleNumberChange(setVal2)} /> {/* Repeat for val3 to val8 */} <input type="number" value={val8} onChange={handleNumberChange(setVal8)} /> <p>Total Duration: {totalDuration}</p> </div> ); }; export default DurationForm;
3. Uncontrolled Components with Refs (Less Ideal, But Useful in Edge Cases)
If you really don't want to use controlled components, you can use useRef to access each input's DOM element directly. Note that this bypasses React's state management, so it's not the recommended approach for most cases—but it's an alternative to querying by ID.
Example Code:
import { useRef, useState } from 'react'; const DurationForm = () => { // Create refs for each input const inputRefs = useRef<(HTMLInputElement | null)[]>(new Array(8).fill(null)); const [totalDuration, setTotalDuration] = useState<number>(0); // Calculate sum when needed (e.g., on button click) const calculateTotal = () => { let sum = 0; inputRefs.current.forEach(ref => { if (ref) { sum += parseFloat(ref.value) || 0; } }); setTotalDuration(sum); }; return ( <div> {Array.from({ length: 8 }).map((_, index) => ( <input key={index} type="number" ref={(el) => (inputRefs.current[index] = el)} defaultValue={0} min="0" /> ))} <button onClick={calculateTotal}>Calculate Total</button> <p>Total Duration: {totalDuration}</p> </div> ); }; export default DurationForm;
Final Recommendations
- Go with the array state approach (method 1) if you want clean, scalable code that aligns with React's best practices.
- Always handle non-numeric inputs to avoid
NaNin your sum—usingparseFloat(...) || 0is a simple, reliable way to do this. - Avoid ID-based DOM queries in React whenever possible; state management is the React-native way to handle form data!
内容的提问来源于stack exchange,提问作者aether

