vladys.bo
vladys.bo

Reputation: 740

State value `undefined` on state onChange

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

Answers (2)

m1kael
m1kael

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

Namish
Namish

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

Related Questions