2

I have 3 label that can receive input when button clicked and then i need sum from the third input value the code kinda work but instead of getting the sum of the third input it instead it looks like the image below where the input get combined kinda like a string (not sure how to call it). Any help is appreciated What Total looks like

This is my full code

import React, { Component } from 'react'; 
import './Bootstrap/css/bootstrap.min.css';


class App extends Component { 
  constructor(){ 
    super(); 
 
    this.state = { 
      mahasiswa : [], 
      tgl : null,
      nama : null, 
      nilai : null,
      nilaiArray: []
    }   
  
  } 
 
  setValueState(event){ 
    var value = event.target.value
    this.setState({ 
      [event.target.name] : value
    }) 
    var nilaiArrayTmp = this.state.nilaiArray;
    nilaiArrayTmp.push(value?parseFloat(value).toFixed(2):0)
    this.setState({
      nilaiArray: nilaiArrayTmp
    })
  }

  addData(){ 
    var data_tmp = this.state.mahasiswa;     
    data_tmp.push({nim : this.state.tgl, nama : this.state.nama, nilai : this.state.nilai}); 
    this.setState({ 
      mahasiswa : data_tmp 
    }) 
    
    
    
  } 
  
   
  render() { 
    const total=(this.state.mahasiswa.reduce((total,{nilai}) =>  total = total + nilai , 0 ));

    return (       
        <div className="container"> 
            <h2><b>Daftar Pengeluaran</b></h2>
            <div className="form-container"> 
                <div className="form-group"> 
                    <label>Waktu:</label> 
                    <input type="text" name="tgl" value={this.state.tgl} onChange={this.setValueState.bind(this)} className="form-control" /> 
                </div> 
                <div className="form-group"> 
                    <label>Deskripsi:</label> 
                    <input type="text" name="nama" value={this.state.nama} onChange={this.setValueState.bind(this)} className="form-control" /> 
                </div> 
                <div className="form-group"> 
                    <label>Jumlah:</label> 
                    <input type="number" name="nilai" value={this.state.nilai} onChange={this.setValueState.bind(this)} className="form-control" /> 
                </div> 
                <br />
                <button onClick={this.addData.bind(this)} type="button" className="btn btn-primary">Tambah</button> 
            </div>
            <br /> 
            <table className="table"> 
                <thead>      
                    <tr>
                        <th>No.</th>
                        <th>Waktu</th>
                        <th>Deskripsi</th>
                        <th>Jumlah</th>
                    </tr> 
                </thead> 
                <tbody> 
                    {this.state.mahasiswa.map((mhs, index)=>( 
                    <tr key={index}> 
                        <td>{index+1}</td>
                        <td>{mhs.tgl}</td> 
                        <td>{mhs.nama}</td>
                        <td>{mhs.nilai}</td>
                    </tr> 
                    ))} 
                    <tr>
                        <td></td>
                        <td></td>
                        <td>Total:</td>
                        <td>{total}</td>

                    </tr>
                </tbody> 
            </table>
        </div> 
    ); 
    } 
  };
  
export default App;
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Unrelated to the totaling issue but you have a state mutation issue in both the `setValueState` and `addData` callbacks. – Drew Reese Nov 30 '21 at 08:06

2 Answers2

1

Input bydefalult is inputting it as string and setting the state also as a String. Because of that 0+string = string and you are getting a string.

Instead of adding to 0, it is converting 0 to a string and concatenating rest of the values to it.

You can use parseInt or parseFloat accordingly to use it as an Integer and get desired result.

const total=(this.state.mahasiswa.reduce((total,{nilai}) => total + parseInt(nilai) , 0 ));
Himanshu Singh
  • 1,803
  • 3
  • 16
1

parseFloat(value).toFixed(2) returns a string type, not a number type

setValueState(event){ 
  var value = event.target.value
  this.setState({ 
    [event.target.name] : value
  }) 
  var nilaiArrayTmp = this.state.nilaiArray;
  nilaiArrayTmp.push(value?parseFloat(value).toFixed(2):0) // <-- converted back to string type!
  this.setState({
    nilaiArray: nilaiArrayTmp
  })
}

so in the array reduce callback you are doing string concatenation. To resolve, convert the values back to a number time for addition. Don't mutate the total value being returned, just return the current total value plus the next nilai value.

const total = this.state.mahasiswa.reduce(
  (total, { nilai }) => total + Number(nilai),
  0
);

The state mutations

In both the setValueState and addData handlers you are mutating the state object but directly pushing into the arrays.

setValueState(event) { 
  var value = event.target.value
  this.setState({ 
    [event.target.name] : value
  }) 
  var nilaiArrayTmp = this.state.nilaiArray; // <-- reference to state
  nilaiArrayTmp.push(value?parseFloat(value).toFixed(2):0) // <-- state mutation!!
  this.setState({
    nilaiArray: nilaiArrayTmp // <-- reference saved back into state
  })
}

addData(){ 
  var data_tmp = this.state.mahasiswa; // <-- reference to state
  data_tmp.push({ // <-- state mutation!!
    nim: this.state.tgl,
    nama: this.state.nama,
    nilai: this.state.nilai,
  }); 
  this.setState({ 
    mahasiswa: data_tmp // <-- reference saved back into state
  });
}

In both cases you should use a functional state update and return a new array reference.

setValueState(event) { 
  const { name, value } = event.target;
  
  this.setState(prevState => ({
    [name]: value,
    nilaiArray: [
      ...prevState.nilaiArray,
      value ? Number(value).toFixed(2) : 0,
    ],
  }))
}

addData(){ 
  this.setState({ 
    mahasiswa: [
      ...prevState.mahasiswa,
      {
        nim: prevState.tgl,
        nama: prevState.nama,
        nilai: prevState.nilai,
      }
    ], 
  });
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181