How can I pass calculated amount to state in react - reactjs

How can I pass currentBook, currentUnits and calculated total during on click on the Record button
Now it just displays the entered value under the purchased book section. I would like to display the data and calculated amount during on click on Record button. Could someone please advise
ie Amount = units * price
for example it should display following result 1. Mathematics 6 300
https://codesandbox.io/s/falling-grass-gwpf9?file=/src/App.js
function App() {
const [currentBook, setCurrentBook] = useState('')
const [currentUnits, setCurrentUnits] = useState('')
const [currentPrice, setCurrentPrice] = useState('')
const [currentRecord, setCurrentRecord] = useState({book:'', units:'', price:''});
const changeBook = (newBook) => {
setCurrentBook(newBook);
}
const changeUnits = (newunits) => {
setCurrentUnits(newunits);
}
const changePrice = (newprice) => {
setCurrentPrice(newprice);
}
const calculateTotal = (e) => {
var cal_total = currentUnits * currentPrice;
setCurrentRecord(currentBook, currentUnits, cal_total );
//setCurrentRecord({ ...currentRecord, [e.target.name]: e.target.value });
}
return (
<div className="App">
<div>
<h1>Online Shopping</h1>
</div>
<div className="flexbox-container">
<div className="maintransaction">
<h3>Choose a book</h3>
<div className="container">
<select defaultValue={'DEFAULT'} onChange={(event) => changeBook(event.target.value)}>
<option value="DEFAULT" disabled>Choose a book ...</option>
<option value="maths">Mathematics</option>
<option value="science">Science</option>
<option value="english">English</option>
<option value="German">German</option>
</select>
</div><br></br>
<div className="quantity">
<span className="units">
<label>Units</label>
<input name="units" type="text" onChange={(event) => changeUnits(event.target.value)}></input>
</span>
<span className="price">
<label>Price</label>
<input name="price" type="text" onChange={(event) => changePrice(event.target.value)}></input>
</span>
</div>
<div className="recordBtn">
<button onClick={(event) => calculateTotal()}>Record</button>
</div>
</div>
<div className="purchasedbooks">
<h3>Purchased book:</h3>
<table>
<tr>
<th>Item no</th>
<th>Books</th>
<th>Units</th>
<th>Price</th>
</tr>
{
//currentRecord.map(({ currentBook, currentUnits }) => (
<tr>
<td>1.</td>
<td>{currentBook}</td>
<td>10</td>
<td>250</td>
</tr>
// ))
}
</table>
</div>
</div>
</div>
);
}
export default App;

Several changes have been made in the sandbox link
Calculate the total when click record as asked in the question
Add some checking when user press the record button. Three inputs need to be filled in.
The input type should be number but not text since it may need to NaN if user enter string in the input.
Implementing a button to reset all the record.
Use map for rendering currentRecord

It looks like your map should look like this:
{currentRecord.map(item => (
<tr>
<td>1.</td>
<td>{item.currentBook}</td>
<td>{item.units}</td>
<td>{item.price}</td>
</tr>
))
}

Related

How can I send a value with Select tag through the useState on ReactJS

I want my code to send the variable from onChange to useState here's my code at for displaying my table on each row
const dataTableElements = entireListData
.slice(0, maxRow)
.map((entireListData, index) => {
var dotStatus = null;
if (entireListData.PriceStatus == true) {
dotStatus = <GreenDot>{entireListData.Price}</GreenDot>;
} else {
dotStatus = <RedDot>{entireListData.Price}</RedDot>;
}
return (
<Tr data-index={index}>
<Td>{entireListData.no}</Td>
<Td>{entireListData.BillNo}</Td>
<Td>{entireListData.Product}</Td>
<Td>
<StatusTableBox>{dotStatus}</StatusTableBox>
</Td>
</Tr>
);
});
next is my select tag
return (
<div>
<Tabbar />
<div>
<p>
Show
<select
value={entireListData.no}
onChange={() => {
sendCurrentRow(entireListData.no);
}}
>
{rowTableElement}
</select>
Entires
</p>
</div>
from the result of the above, it shows in console "undefined".
Usually the select tag gives an event in the onChange method that you can use to extract the value which selected, take a look at this example:
<select
value={entireListData.no}
onChange={(event) => {
sendCurrentRow(event.target.value);
}}
>
//here should be your options with different values
<option value="1">
1
</option>
<option value="2">
2
</option>
// I don't know this value has the option or not => {rowTableElement}
</select>

Weird return of data after splice and setstate

Here's simulate in codesandbox
https://codesandbox.io/embed/epic-nash-mxteu?fontsize=14&hidenavigation=1&theme=dark
I am having a weird behavior when I remove a row from the dynamic rows created.
I have removed the row with 2 b. As you can see the console log has the correct data while the UI showing wrong data. It means the function works well but somehow displaying incorrectly.
Anyone have idea why? Thanks in advance
Screenshots
Before remove row
After remove row
Source code
const [gavRows, setGAVRows] = useState([]);
const handleGAVAddRow = async () => {
try {
const item = {
gav_field: '',
gav_value: ''
};
setGAVRows([...gavRows, item]);
} catch (error) {
console.log('error', error)
}
};
const handleGAVRemoveSpecificRow = (idx) => {
console.log('idx', idx)
const tempRows = [...gavRows];
console.log('tempRows', tempRows)
tempRows.splice(idx, 1);
setGAVRows(tempRows)
};
const handleGAVChange = async (idx, e) => {
const { name, value } = e.target;
var tempRows = [...gavRows];
tempRows[idx][name] = value;
setGAVRows(tempRows)
};
<table className="customgav_section">
<tbody>
{
gavRows.map((item, idx) => {
console.log('map idx', idx, item)
return (
<tr key={idx}>
<td>
<Input type="text"
name="gav_field" id="gav_field"
value={gavRows[idx].field}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>
<td>
<Input type="text"
name="gav_value" id="gav_value"
value={gavRows[idx].value}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>
<td>
<Button outline color="danger" onClick={() => handleGAVRemoveSpecificRow(idx)}><FaMinus /></Button>
</td>
</tr>)
})
}
</tbody>
</table>
Your problem is that you are using the index of the array as the key.
Read why that is bad: https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318
A quick hack was assigning a random number as the key of each item in the gavRows and using that as the key of the element. See updated code: https://codesandbox.io/s/charming-bouman-03zn7
Also, the id of an element must be unique in the DOM so i removed those from the input elements.
Codesandbox
The problem is you put the wrong input value.
Remember the item object you set is:
const item = {
gav_field: "", //not field
gav_value: "" //not value
};
You should modify code from
<td>
<Input
type="text"
name="gav_field"
id="gav_field"
value={gavRows[idx].field}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>
<td>
<Input
type="text"
name="gav_value"
id="gav_value"
value={gavRows[idx].value}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>
To:
<td>
<Input
type="text"
name="gav_field"
id="gav_field"
value={gavRows[idx].gav_field}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>
<td>
<Input
type="text"
name="gav_value"
id="gav_value"
value={gavRows[idx].gav_value}
onChange={(e) => handleGAVChange(idx, e)}
/>
</td>

every accordion opens when i click on any accordion : react js

every accordion opens when I click on any accordions can anyone help with this I want only one to open at a time
const [isActive, setIsActive] = useState(false);
<div className="accordion">
{Object.values(mapped).map((item, index) => (
//{predefined.map(({ date }) => (
<div className="accordion-item">
<div
className="accordion-title"
onClick={() => setIsActive(!isActive)}
>
<div>{item[0].date}</div> <div>11:45</div>
<div>painscale</div>
<div>4</div>
<div>{isActive ? "-" : "+"}</div>
</div>
{isActive && (
<div className="accordion-content">
<table>
<thead>
<tr>
<th>sdcs</th>
</tr>
</thead>
{item.map((e) => {
return (
<tr>
<td> {e.time}</td> <td>{e.d}</td>{" "}
<td> {e.scale}</td> <td>{escale.}</td>
</tr>
);
})}
</table>
</div>
)}
</div>
))}
</div>
every accordion opens when I click on any accordions can anyone help with this I want only one to open at a time
This is because the isActive state is a general one to the component and therefore would control all other elements in that component together.
However, to achieve what you're looking for, you can change that to an active current Index like so:
const [activeCurrentIndex, setActiveCurrentIndex] = useState();
const toggleShowAccordion = (id) => {
if(activeCurrentIndex === id){
setActiveCurrentIndex();
} else {
setActiveCurrentIndex(id);
}
}
//....
<div className="accordion">
{predefined.map(({ id, date, time , pain_scale, time_stamp})) => (
<div className="accordion-item" key={id}>
<div className="accordion-title" onClick={() => toggleShowAccordion(id)}>
<div>{date}</div>
<div>{isActive ? "-" : "+"}</div>
</div>
{activeCurrentIndex === id && <div className="accordion-content">{time} {pain_scale} {time_stamp}</div>}
</div>
))}
</div>
You're setting the activeCurrentIndex to that of the accordion you want to expand and you check if the activeCurrentIndex state is same as accordion's id to expand the menu otherwise, it's closed. You also need to supply a key to each element while mapping through an array

Disable all checkboxes except "checked" react hooks

I have listing of products and user can compare upto 4 products, when user checked 4 products I want to disable all checkboxes so user cannot select other product for compare until unless it uncheck one of 4 checkboxes.
const [checkedddItems, setCheckedItems] = useState({checkedItems : {}})
const handleComparePackage = (e, packageId) => {
const { id, checked } = e.target;
const updatedCheckedItems = comparedPackages.includes(packageId)? { [id]: checked } : {checkedddItems, [id] : checked }
console.log(updatedCheckedItems);
setCheckedItems({checkedItems: updatedCheckedItems})
}
{ insurancePackages.map((insPackage) => {
return (
<div className="col-lg-3 col-md-4 col-sm-6" key={insPackage.id}>
<div className="insurance-card active">
{compareSwitch &&
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
id={insPackage.id}
checked={checkedddItems[insPackage.id]}
disabled={!checkedddItems[insPackage.id]}
onChange={(e) => { handleComparePackage(e, insPackage.id) }} />
</div>
}
<div className="thumb">
<img src="/insurance/logo.svg" alt="logo" />
</div>
<div className="title">
{insPackage.company.name}
</div>
<div className="text-detail">
{insPackage.description}
<br />
<Link href="/">
<a>View Package Details</a>
</Link>
</div>
</div>
)
})
}
From what I can tell, it seems like you need some way to disable all checkboxes when the following conditions are met:
the checkbox is not checked,
the amount of total checked items > 3
This should simply turn into a simple boolean statement in the disabled attribute on the checkbox <input/>.
<input
className="form-check-input"
type="checkbox"
id={insPackage.id}
checked={checkedddItems[insPackage.id]}
disabled={!checkedddItems[insPackage.id] && checkedddItems.length > 3} // right here
onChange={(e) => { handleComparePackage(e, insPackage.id) }} />

React - List?Dictionary?State? Or am i missing something

Let's start off with the problem I'm having and telling you guys what I would like to achieve.
First of all, I'm getting this error
Warning: A component is changing a controlled input of type text to be uncontrolled.
Input elements should not switch from controlled to uncontrolled (or vice versa).
Decide between using a controlled or uncontrolled input element for the lifetime of the component.
My goal is to save data first to the state. Or I should use List, or dictionary? This is where I'm stuck. I will post my code here also for you to check what I'm doing wrong or what should I do differently.
import React from 'react'
import './TableData.css'
class TableData extends React.Component{
constructor(props){
super(props)
this.state = {
rows:[{service: '',
quantity: '',
price: '',
sum: ''}]
}
this.handleChange = this.handleChange.bind(this)
this.handleAddRow = this.handleAddRow.bind(this)
this.handleRemoveRow = this.handleRemoveRow.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange = idx => event => {
var rows = [...this.state.rows]
rows[idx] = {
[event.target.name]: event.target.value
}
this.setState({
rows
})
var data = this.state.rows
console.log("Log me", data)
}
handleAddRow = () => {
var item = {
service: '',
quantity: '',
price: '',
sum: ''
}
this.setState({
rows: [...this.state.rows, item]
})
}
handleRemoveRow = () => {
this.setState({
rows: this.state.rows.slice(0, -1)
})
}
handleSubmit = (event) => {
event.preventDefault()
var tableData = this.state.rows
console.log("Final data is:", tableData)
}
render() {
return (
<div className="tablePos container" >
<form onSubmit={this.handleSubmit}>
<div className="row">
<table id="tab_logic">
<thead className="tableBackground">
<tr>
<th className="col-md-auto"> Service </th>
<th className="col col-lg-2"> Quantity </th>
<th className="col col-lg-2"> Price </th>
<th className="col col-lg-2"> Sum </th>
</tr>
</thead>
<tbody>
{this.state.rows.map((item, idx) => (
<tr key={idx}>
<td>
<input className="form-control" type="text" name="service" placeholder="Ex: Cloud Service" value={this.state.rows[idx].service} onChange={this.handleChange(idx)}/>
</td>
<td>
<input className="form-control" type="text" name="quantity" placeholder="Ex: 2 Month" value={this.state.rows[idx].quantity} onChange={this.handleChange(idx)}/>
</td>
<td>
<input className="form-control" type="text" name="price" placeholder="Ex: 75.00" value={this.state.rows[idx].price} onChange={this.handleChange(idx)}/>
</td>
<td>
<input className="form-control" type="text" name="sum" placeholder="Ex: 150.00" value={this.state.rows[idx].sum} onChange={this.handleChange(idx)} />
</td>
</tr>
))}
</tbody>
</table>
</div>
<button>Send Data!</button>
</form>
<button onClick={this.handleAddRow} className="btn btn-success">Add Row</button>
<button onClick={this.handleRemoveRow} className="btn btn-danger">Delete Last Row</button>
</div>
);
}
}
export default TableData
So basically it creates 4 input boxes and then you can write in and if you are done you click Send Data it saves it to state or add new row and then it will add new row for you to input data. What I do get is the following from that code.
Console log picture of the data
It only saves the last input field data when I click send data not all of them.
Sorry about my messy explanation but I hope you did understand my problem and thank you for your replies!
while assigning the values inside onChange. You are spreading the array as needed. But you have to spread the object too.. Otherwise, it will just assign the last-changed-input-field-value to the object.
rows[idx] = {
...this.state.rows[idx],
[event.target.name]: event.target.value
};
You can find my code below.
https://codesandbox.io/s/small-dew-wjqqi

Resources