I'm using a form and two spans to increment/decrement multiple inputs. Each time the form is modified (determined thru onclick), I want to update the sum accordingly. I tried implementing the setState function correctly so as to account for React's state call batching; but it doesn't seem to help (if you run this with console.log, you'll notice that the information is a step behind). I believe it stems from trying to get the value of inputField
(top of onClick function & very scuffed implementation). However, I'm not sure how to get and increment this information otherwise. I'm relatively new to React, so even some insight on how I should be structuring this would be helpful (as this poor design seems to be the root of my issues).
- Thanks in advance
import React, { Component, PureComponent } from "react";
function App() {
return (
<div className="App">
<Parent />
</div>
);
}
class Parent extends PureComponent {
state = {
formState: {
// list all objectives here
firstObjective: [0, 10],
secondObjective: [0, 30],
thirdObjective: [0, 1],
},
runningTotal: 0,
};
onClick = event => {
var inputField = event.target;
while (inputField.tagName !== 'INPUT') {
inputField = inputField.nextElementSibling;
}
this.setState((prevState) => {
return {
formState: {
...prevState.formState,
[inputField.name]: [
(event.target.className === 'arrow down') ?
Number(Math.max(inputField.value - 1, 0)) :
Number(inputField.value) 1,
Number(inputField.className)
]
}
}
});
// ref all objectives here
var objectives = [
this.state.formState.firstObjective,
this.state.formState.secondObjective,
this.state.formState.thirdObjective
];
console.log(objectives);
var sum = 0;
for (let i = 0; i < objectives.length; i ) {
sum = Math.max(Number(objectives[i][0]) - 1, 0) * Number(objectives[i][1]);
}
this.setState({
runningTotal: sum
});
}
render() {
const { formState } = this.state;
return (
<div>
<Form
formState={formState}
onClick={this.onClick}
runningTotal={this.state.runningTotal}
/>
</div>
);
}
}
class Field extends Component {
state = { inputFieldValue: 0, }
increment(event) {
this.props.onClick(event, this.inputFieldValue);
}
render() {
return (
<div>
<div className="field">
<label htmlFor={this.props.name}>{this.props.label}</label>
<div className="numInput">
<span className="arrow up" onClick={this.props.onClick} />
<span className="arrow down" onClick={this.props.onClick} />
<input type="text" name={this.props.name} value={this.props.value} className={this.props.className} readOnly />
</div>
</div>
</div>
);
}
}
class Form extends Component {
render() {
return (
<>
<form className="form">
<fieldset>
<Field
name="firstObjective" // must match firstObjective
label="First Objective"
value={this.props.formState.firstObjective[0]}
className={this.props.formState.firstObjective[1]}
onClick={this.props.onClick} />
<Field
name="secondObjective" // must match secondObjective
label="Second Objective"
value={this.props.formState.secondObjective[0]}
className={this.props.formState.secondObjective[1]}
onClick={this.props.onClick} />
<Field
name="thirdObjective" // must match third objective
label="Third Objective"
value={this.props.formState.thirdObjective[0]}
className={this.props.formState.thirdObjective[1]}
onClick={this.props.onClick} />
</fieldset>
</form>
<div className="total">Total: {this.props.runningTotal}</div>
</>
);
}
};
export default App;
CodePudding user response:
You need not keep runningTotal
in your state because it's calculated from other state values.
You can define a function in Parent
component to calculate the runningTotal
const getRunningTotal = () => {
const objectives = [
this.state.formState.firstObjective,
this.state.formState.secondObjective,
this.state.formState.thirdObjective,
];
let sum = 0;
for (let i = 0; i < objectives.length; i ) {
sum = Math.max(Number(objectives[i][0]) - 1, 0) * Number(objectives[i][1]);
}
return sum;
};
And then use it to provide prop for Form
component
<Form
formState={formState}
onClick={this.onClick}
runningTotal={this.getRunningTotal()}
/>