Reputation: 740
Writing a component named Member
where I'm doing some calculations which I store in this.state
before sending it somewhere else, but I got a problem, when firing one of onChange
handlers some values of state are undefined
, but should be filled some value.
Component Member
import React from "react";
import {calcSCR, difference, multiply} from "../../utils/Calculations";
import {SCR_COEFF} from "../../constants/calc";
class Member extends React.Component {
constructor(props){
super(props);
this.state = {
id: "1",
full_name: 'Mocked member 1',
role: 'Developer',
salary: 1650,
hourly_cost: 19.6, // SCR = (gross * coeff) / 100
project_data: {
project_id: '1',
hourly_rate: 40,
occupation: 50,
estimate_hours: 70,
revenue: 2800,// revenue = estimate_hours * hourly_rate
cost: 1372,// cost = estimate_hours * hourly_cost
profit: 1428// profit = revenue - cost
}
};
this.onSalaryChange = this.onSalaryChange.bind(this);
this.onHourlyCostChange = this.onHourlyCostChange.bind(this);
this.onHourlyRateChange = this.onHourlyRateChange.bind(this);
this.onEstimateHoursChange = this.onEstimateHoursChange.bind(this);
this.onRevenueChange = this.onRevenueChange.bind(this);
this.onCostChange = this.onCostChange.bind(this);
this.onNameChange = this.onNameChange.bind(this);
this.onOccupationChange = this.onOccupationChange.bind(this);
this.save = this.save.bind(this);
}
save(){
}
onNameChange(e){
this.setState({
full_name: e.target.value
})
}
onSalaryChange(e){
let salary = e.target.value;
this.setState({
salary: salary,
hourly_cost: calcSCR(salary, SCR_COEFF)
})
}
onHourlyRateChange(e){
let hourly_rate = e.target.value;
let estimate_hours = this.state.project_data.estimate_hours;
this.setState({
project_data: {
hourly_rate: hourly_rate,
revenue: multiply(estimate_hours, hourly_rate)
}
})
}
onOccupationChange(e){
this.setState({
project_data: {
occupation: e.target.value
}
})
}
onEstimateHoursChange(e){
let estimate_hours = e.target.value;
this.setState({
project_data: {
estimate_hours: estimate_hours,
revenue: multiply(estimate_hours, this.state.project_data.hourly_rate),
cost: multiply(estimate_hours,this.state.hourly_cost)
}
})
}
onHourlyCostChange(e){
let hourly_cost = e.target.value;
this.setState({
hourly_cost: hourly_cost,
project_data: {
cost: multiply(this.state.project_data.estimate_hours, hourly_cost)
}
})
}
onRevenueChange(e){
let revenue = e.target.value;
this.setState({
project_data: {
revenue: revenue,
profit: difference(revenue,this.state.project_data.cost)
}
})
}
onCostChange(e){
let cost = e.target.value;
this.setState({
project_data: {
cost: cost,
profit: difference(this.state.project_data.revenue,cost)
}
})
}
render(){
let {projectName} = this.props;
let data = this.state;
return (
<div className="member">
<a className="member__short_info" role="button" data-toggle="collapse" href={`#${data.id}`} aria-expanded="false" aria-controls={data.id}>
{data.full_name}
</a>
<div className="collapse member__member_full_info" id={data.id}>
<div className="member__full_info__row">
<div className="row">
<div className="col-sm-12 col-md-12 com-lg-12 member__full_info__header">
{projectName}
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label htmlFor="full_name" className="member__full_info__label">Member full name:</label>
<input id="full_name" type="text" name="full_name" value={data.full_name} onChange={this.onNameChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label htmlFor="salary" className="member__full_info__label">Salary:</label>
<input id="salary" type="text" name="salary" value={data.salary} onChange={this.onSalaryChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label className="member__full_info__label">Hourly cost: </label>
<input id="hourly_cost" type="text" name="hourly_cost" value={data.hourly_cost} onChange={this.onHourlyCostChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label htmlFor="hourly_rate" className="member__full_info__label">Hourly rate</label>
<input id="hourly_rate" type="text" name="hourly_rate" value={data.project_data.hourly_rate} onChange={this.onHourlyRateChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label htmlFor="occupation" className="member__full_info__label">Occupation in project (%): </label>
<input id="occupation" type="text" name="occupation" value={data.project_data.occupation} onChange={this.onOccupationChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-6 member__full_info__column">
<label htmlFor="estimate_hours" className="member__full_info__label">Estimate hours: </label>
<input id="estimate_hours" type="text" name="estimate_hours" value={data.project_data.estimate_hours} onChange={this.onEstimateHoursChange}/>
</div>
<div className="col-sm-12 col-md-12 col-lg-4 member__full_info__column">
<label className="member__full_info__label">Revenue: {data.project_data.revenue}</label>
{/*<input id="revenue" type="number" name="revenue" value={data.project_data.revenue} onChange={this.onRevenueChange}/>*/}
</div>
<div className="col-sm-12 col-md-12 col-lg-4 member__full_info__column">
<label className="member__full_info__label">Cost: {data.project_data.cost}</label>
{/*<input id="cost" type="number" name="cost" value={data.project_data.cost} onChange={this.onCostChange}/>*/}
</div>
<div className="col-sm-12 col-md-12 col-lg-4 member__full_info__column">
<label className="member__full_info__label">Profit: {data.project_data.profit}</label>
</div>
<div className="col-sm-12 member__full_info__save">
<button className="modal-component positive-btn no-margin" onClick={this.save}>Save</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default Member;
For example: inside onHourlyRateChange
function estimate_hours
becomes undefined
after change.
Inside onEstimateHoursChange
state variables this.state.project_data.hourly_rate
and this.state.project_data.hourly_cost
becomes undefined
too. I have no Idea why it happens. Maybe you can give me some advices?
Upvotes: 0
Views: 133
Reputation: 2851
You can partially update top-level properties of a state object, but nested updates won't work.
immutability-helper's update
(replacement for react-addons-update) can be used to update nested properties:
import update from 'immutability-helper';
const newData = update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
Upvotes: 1
Reputation: 114
Looks like all your nested state is experiencing problems, i.e., nested inside project_data. Component is only updating child elements one time, i.e. on your first key press and for all subsequent key presses, it is not updating anything, instead values goes undefined for nested state items which is very strange. A work around for this can be like you can move all your nested state items as single level state items. Ex:
this.state = {
project_data: { hourly_rate: 40 }
}
instead you can move child item to parent
this.state = {
project_data_hourly_rate: 40
}
Upvotes: 1