Home > Software engineering >  Getting sum of total value from Reduce
Getting sum of total value from Reduce

Time:11-30

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;

CodePudding user response:

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 ));

CodePudding user response:

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,
      }
    ], 
  });
}
  • Related