The following code has a button to add inputs and another button to add all of the values in the inputs assuming they are numbers. The last line is to display the sum of everything. However, for some reason it only gets the value of the last input. It also does not reset. I thought having the setGrade(0) would do this but it just keeps adding the last number without resetting.
I would just like to know why this is the case with the following code. The id for the input fields are just the number starting from 1.
function Start(){
const [rows, setRows] = useState([]);
const [inputNum,setNum] = useState(1);
const [totalGrade, setGrade] = useState(0);
const addInput = () =>{
setNum(inputNum+1);
setRows(rows.concat(<Inputs key={rows.length} label={inputNum.toString()}></Inputs>));
}
const addGrade = () =>{
setGrade(0);
for(let i =0;i<rows.length;i++){
setGrade(parseInt(document.getElementById((i+1).toString()).value,10) +totalGrade)
}
}
return(
<div>
<h1>Calculate your GPA!</h1>
{rows}
<button class="button2"onClick={addInput}>Add Input</button>
<button class="button2"onClick={addGrade}>Compute Grade</button>
<h2>Grade: {totalGrade}</h2>
</div>
);
}
You shouldn't be mixing native element methods like getElementById
in React code.
Add an onChange
directly onto the input elements.
Create a new state (an object) that maintains a record of each change to an input (identified by key/id) where the value is the new value of the input.
Don't set state in a loop - it's bad practice.
Here's how I've approached it (I've had to simplify the example as I don't have access to the Inputs
component.)
const { useEffect, useState } = React;
function Example() {
const [inputNum, setInputNum] = useState(0);
// The new state which maintains all the input values
const [inputData, setInputData] = useState({});
const [totalGrade, setTotalGrade] = useState(0);
// `addInput` is now only responsible
// for updating the number of rows
function addInput() {
setInputNum(inputNum + 1);
}
// NEW FUNCTION: it handles the update of the
// `inputData` state. It grabs the id and value from
// the input, and then updates the state with that
// new information
function handleChange(e) {
const { id, value } = e.target;
// Take the previous state (object), and update it
// by spreading (copying) out the previous object,
// and adding a new property with the id as key
// and the value as the value.
setInputData(prev => ({ ...prev, [id]: value }));
}
// `sumGrade` - I renamed this - grabs the Object.values
// of the inputData state and then creates a sum of all
// of those values using `reduce`. It then, finally, sets
// the `totalGrade` state.
function sumGrade() {
const values = Object.values(inputData);
const result = values.reduce((acc, c) => {
return acc + +c;
}, 0);
setTotalGrade(result);
}
// NEW FUNCTION: this builds an array of new inputs
// which can be used in the JSX
function buildRows() {
const arr = [];
for (let i = 0; i < inputNum; i++) {
arr.push(<input onChange={handleChange} type="number" key={i} id={i} value={inputData[i]}/>);
}
return arr;
}
return (
<div>
<h1>Calculate your GPA!</h1>
{buildRows()}
<button class="button2"onClick={addInput}>Add Input</button>
<button class="button2"onClick={sumGrade}>Compute Grade</button>
<h2>Grade: {totalGrade}</h2>
</div>
);
}
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
input { display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>