In my react app, I have created a page named Add Product with a button named Add Variation to allow adding small, medium, large variations of a product but can't figure out how to remove the small, medium, or large variation object from the state if user changes their mind.
Here's a summary of the problem:
Here's what the component looks like now:
const AddProduct = () => {
const [addVar, setAddVar] = useState(0)
const [values, setValues] = useState({
name: "",
description: "",
categories: [],
category: "",
photo: "",
loading: false,
error: "",
createdProduct: "",
redirectToProfile: false,
variations: [],
formData: ""
});
const {
name,
description,
price,
categories,
category,
photo,
loading,
error,
createdProduct,
redirectToProfile,
variations,
formData
} = values;
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
const handleVariationChange = (name, numberVal) => event => {
// numberVal is the iteration number
// name is the variation property which can be vname, vprice, vshipping, vquantity
// these are tested next in the following if statements
const value = event.target.value;
console.log(`numberVal: `, numberVal);
event.preventDefault()
let newVariations = Array.from(variations)
if(name === "vname") {
newVariations[numberVal].vname = value;
console.log(`newVariations[numberVal].vname value: `, newVariations)
}
if(name === "vprice") {
newVariations[numberVal].vprice = value;
console.log(`newVariations[numberVal].vprice value: `, newVariations)
}
if(name === "vshipping") {
newVariations[numberVal].vshipping = value;
console.log(`newVariations[numberVal].vshipping value: `, newVariations)
}
if(name === "vquantity") {
newVariations[numberVal].vquantity = value;
console.log(`newVariations[numberVal].vquantity value: `, newVariations)
}
setValues({...values, variations: newVariations})
formData.set("variations", JSON.stringify(newVariations));
};
const removeVariation = (e) => {
e.preventDefault()
let newVariations = Array.from(variations)
let popped = newVariations.pop()
setValues({
...values,
variations: newVariations,
error: ""
})
}
const newPostForm = () => (
<form className="mb-3" onSubmit={clickSubmit}>
<h4>Main Photo</h4>
<div className="form-group">
<label className="btn btn-secondary">
<input
onChange={handleChange("photo")}
type="file"
name="photo"
accept="image/*"
/>
</label>
</div>
<div className="form-group">
<label className="text-muted">Main Product Name</label>
<input
onChange={handleChange("name")}
type="text"
className="form-control"
value={name}
placeholder="Add main product name"
/>
</div>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea
onChange={handleChange("description")}
className="form-control"
value={description}
placeholder="Add description"
/>
</div>
<div className="form-group">
<label className="text-muted">Category</label>
<select
onChange={handleChange("category")}
className="form-control"
>
<option>Please select</option>
{categories &&
categories.map((c, i) => (
<option key={i} value={c._id}>
{c.name}
</option>
))}
</select>
</div>
<div>
<button onClick={addVariation}>Add variation</button>
</div>
{variations ? VariationComponent() : null}
<br />
<br />
<button type="submit" className="btn btn-outline-primary">Create Product</button>
</form>
);
return (
<Layout>
<div className="row">
<div className="col-md-8 offset-md-2">
{newPostForm()}
</div>
</div>
</Layout>
);
};
export default AddProduct;
Every time Add variation is clicked, another VariationComponent form is appended to the page . For example, if Add variation button was clicked 3 times, it would result in 3 VariationComponent forms with 3 attached Remove variation buttons. Unfortunately, I do not see how to tell React the position of the #2 item in variations to remove it so I resorted to solving this with .pop(), which is not what I want.
How can I tell React to remove the right array item when Remove variation button is clicked?
If I understand correctly, you can use Arrray.filter() determine which variation to remove. It returns a new array with all but the matching numberVal.
onClick={e=>removeVariation(e)}
const removeVariation = e => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.name !== e.target.value),
error: ''
});
};
Thanks to #RobinZigmond's and #7iiBob's answers, I was able to solve this by this code:
const removeVariation = (e, num) => {
e.preventDefault();
setValues({
...values,
variations: variations.filter(item => item.number !== num),
error: ''
});
};
Remove variation button:
<button onClick={(e) => removeVariation(e, variations[i].number)} className="btn-danger">
{`Remove Variation`}
</button>
Keep in mind the empty variation object looks like this:
{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}
and n is coming from addVariation here:
const addVariation = (e) => {
e.preventDefault()
setAddVar(addVar + 1)
let oldV = Array.from(variations); // gets current variations
let n = oldV.length; // get current array position
console.log(`Current number of variations is: ${n}`);
let vPost = [{
number: n,
vname: "",
vprice: "",
vquantity: "",
vshipping: ""
}]
let newV = oldV.concat(vPost);
setValues({
...values,
variations: newV,
error: ""
})
}
Wholehearted thank you as this cost me hours of headache!
Related
I have a component renderRoyaltyAccount, that gets rendered x number of times depending on the input that sets royaltyAccount.
In this component I have 2 fields, one for the name of the account, and the second a percentage.
What I wanted to do is depending of the number of accounts to create, create an object with those two fields for each, example :
If he chooses to create two accounts , to have a the end (what I thought but could be not the best choice :) ) :
{
1: {
"account": "test1",
"percentage": 2,
},
2: {
"account": "test#",
"percentage": 0.5
}
}
I tried with a useState and updating it with onChange with inputs, but it was a mess LOL.
If anyone could help me with this state, and specially the logic with objects and hooks. Thank you
export default function FormApp() {
const [royaltyAccount, setRoyaltyAccount] = useState(1);
const [allAccounts, setAllAccounts] = useState ({
{
"account": "",
"percentage": 1,
},
})
const renderRoyaltyAccounts = () => {
let items = [];
for (let i = 0; i < royaltyAccount; i++) {
items.push(
<div key={i}>
<div>
<label>Royalty Account n° {i + 1}</label>
<input onChange={()=> setAllAccounts(???)} type="text"/>
</div>
<div>
<label>Royalty %</label>
<input onChange={()=> setAllAccounts(???)} type="text"/>
</div>
</div>
)
}
return items;
}
return (
<>
<label> Royalty account(s)</label>
<input onChange={(e) => { setRoyaltyAccount(e.target.value)}} type="number"/>
{
renderRoyaltyAccounts()
}
</>
)
}
Dynamically compute the allAccounts state array from the initial royaltyAccount state value. Add an id property to act as a GUID for each account object.
Create a handleRoyaltyAccountChange onChange handler to either append a computed diff of the current allAccounts array length to the new count value, or to slice up to the new count if less.
Create a handleAccountUpdate onChange handler to shallow copy the allAccounts state array and update the specifically matching account object by id.
Give the inputs a name attributeand pass the mappedallAccountselement object's property as thevalue` prop.
Code:
import { useState } from "react";
import { nanoid } from "nanoid";
function FormApp() {
const [royaltyAccount, setRoyaltyAccount] = useState(1);
const [allAccounts, setAllAccounts] = useState(
Array.from({ length: royaltyAccount }).map(() => ({
id: nanoid(),
account: "",
percentage: 1
}))
);
const handleRoyaltyAccountChange = (e) => {
const { value } = e.target;
const newCount = Number(value);
setRoyaltyAccount(newCount);
setAllAccounts((accounts) => {
if (newCount > accounts.length) {
return accounts.concat(
...Array.from({ length: newCount - accounts.length }).map(() => ({
id: nanoid(),
account: "",
percentage: 1
}))
);
} else {
return accounts.slice(0, newCount);
}
});
};
const handleAccountUpdate = (id) => (e) => {
const { name, value } = e.target;
setAllAccounts((accounts) =>
accounts.map((account) =>
account.id === id
? {
...account,
[name]: value
}
: account
)
);
};
return (
<>
<label> Royalty account(s)</label>
<input
type="number"
onChange={handleRoyaltyAccountChange}
value={royaltyAccount}
/>
<hr />
{allAccounts.map((account, i) => (
<div key={account.id}>
<div>
<div>Account: {account.id}</div>
<label>
Royalty Account n° {i + 1}
<input
type="text"
name="account"
onChange={handleAccountUpdate(account.id)}
value={account.account}
/>
</label>
</div>
<div>
<label>
Royalty %
<input
type="text"
name="percentage"
onChange={handleAccountUpdate(account.id)}
value={account.percentage}
/>
</label>
</div>
</div>
))}
</>
);
}
I've created script for dynamic form, but there's 2 things which I can't get and my head is exploding right now, hopefully somebody would help me with that.
After creating new fields - I can't remove fields depends on button which was clicked.
And after removing some of those fields, I have this error with fieldsenter image description here
import React from "react";
import {useState , useEffect} from "react";
import ReactDOM from "react-dom";
import "./index.css";
const Form = () =>{
const [fieldsLength, fieldsLengthChanger] = useState(1);
const [fields, fieldsChanger] = useState([{
id : 1,
name: "",
phone: "",
age: ""
}])
return (
<>
<div className="form__wrapper">
<h2>Form </h2>
{
fields.map((elem, index) => {
return(
<FormElement {...elem} fields={fields} fieldsChanger={fieldsChanger} fieldsLength={fieldsLength} fieldsLengthChanger={fieldsLengthChanger}/>
)
})
}
<AddMore fieldsLength={fieldsLength} fieldsLengthChanger={fieldsLengthChanger} fields={fields} fieldsChanger={fieldsChanger}/>
</div>
</>
)
}
const FormElement = ({fieldsLength ,...props}) =>{
function inputHandler(e, id){
console.log(e.target.name);
const values = [...props.fields];
values[id-1][e.target.name] = e.target.value;
props.fieldsChanger(values);
}
function removeElement(e,id){
e.preventDefault();
var arr = [...props.fields];
const newArray = arr.filter(function(elem,index){
console.log("index:" , index, "ID : ", id);
if (index + 1 != id){
return elem;
}
});
// console.log(newArray);
// arr.splice(3, 1);
props.fieldsChanger([])
props.fieldsChanger(newArray);
}
return (
<div className="group__form">
<div className="form__element">
<input type="text" value={props.fields.name} name="name" onChange={e => inputHandler(e , props.id)} />
</div>
<div className="form__element">
<input type="text" value={props.fields.phone} name="phone" onChange={e => inputHandler(e , props.id)}/>
</div>
<div className="form__element">
<input type="text" value={props.fields.age} name="age" onChange={e => inputHandler(e , props.id)}/>
</div>
{
fieldsLength > 1 ? <div className="remove__field">
<a href="#" onClick={e=>removeElement(e , props.id)}>Remove</a>
</div> : ""
}
</div>
)
}
const AddMore = (props) =>{
function addMore(){
props.fieldsLengthChanger(props.fields.length + 1);
props.fieldsChanger([...props.fields, {id:props.fields.length + 1 , name: "" , phone : "" , age :''} ]);
}
return (
<div className="add__more">
<a href="#" onClick={e=> addMore()}>Add element</a>
</div>
)
}
ReactDOM.render(<Form/> , document.getElementById("root"));
Where I'm wrong - would be really helpfull to understand what is the problem
Always use key when rendering list.
<FormElement key={elem.id} {...elem} ...
https://reactjs.org/docs/lists-and-keys.html#keys
1. You should use the props.id to remove a form instead of using index.
e.g. this must be a bug.
if (index + 1 != id) {
return elem;
}
2. And you should use id by the unique value generation instead of using array's length.
3. You should provide the key prop in the render of the form fields. Otherwise, React can't distinguish forms in rendering.
And the key should be unique whenever you add or remove items.
e.g. See the updated code. You need to use elem.id. (Of course, you should generate id in the add method.
{fields.map((elem, index) => {
return (
<FormElement
key={elem.id}
{...elem}
fields={fields}
fieldsChanger={fieldsChanger}
fieldsLength={fieldsLength}
fieldsLengthChanger={fieldsLengthChanger}
/>
);
})}
4. You could basically use a timestamp. Please use your own generation logic in the production. You can use uuid generation package.
{
id: new Date().getTime(),
name: "",
phone: "",
age: ""
}
My advices:
You don't need to use this state variable const [fieldsLength, fieldsLengthChanger] = useState(1);
Because we can get this value by fields.length.
There are a few problems in terms of components design. e.g. Please try to define addMore() in form then you don't need to pass fields, fieldsChanger as props.
Here is a full working code with your old code commented.
const Form = () => {
const [fieldsLength, fieldsLengthChanger] = useState(1);
const [fields, fieldsChanger] = useState([
{
id: new Date().getTime(),
name: "",
phone: "",
age: ""
}
]);
return (
<>
<div className="form__wrapper">
<h2>Form </h2>
{fields.map((elem, index) => {
return (
<FormElement
key={elem.id}
{...elem}
fields={fields}
fieldsChanger={fieldsChanger}
fieldsLength={fieldsLength}
fieldsLengthChanger={fieldsLengthChanger}
/>
);
})}
<AddMore
fieldsLength={fieldsLength}
fieldsLengthChanger={fieldsLengthChanger}
fields={fields}
fieldsChanger={fieldsChanger}
/>
</div>
</>
);
};
const FormElement = ({ fieldsLength, ...props }) => {
function inputHandler(e, id) {
console.log(e.target.name);
const values = [...props.fields];
const item = values.find((x) => x.id === props.id);
if (item) {
item[e.target.name] = e.target.value;
}
// values[id - 1][e.target.name] = e.target.value;
props.fieldsChanger(values);
}
function removeElement(e, id) {
e.preventDefault();
var arr = [...props.fields];
const newArray = arr.filter(function (elem, index) {
/*console.log("index:", index, "ID : ", id);
if (index + 1 != id) {
return elem;
}*/
return elem.id !== id;
});
// console.log(newArray);
// arr.splice(3, 1);
props.fieldsChanger([]);
props.fieldsChanger(newArray);
}
return (
<div className="group__form">
<div className="form__element">
<input
type="text"
value={props.fields.name}
name="name"
onChange={(e) => inputHandler(e, props.id)}
/>
</div>
<div className="form__element">
<input
type="text"
value={props.fields.phone}
name="phone"
onChange={(e) => inputHandler(e, props.id)}
/>
</div>
<div className="form__element">
<input
type="text"
value={props.fields.age}
name="age"
onChange={(e) => inputHandler(e, props.id)}
/>
</div>
{fieldsLength > 1 ? (
<div className="remove__field">
<a href="#" onClick={(e) => removeElement(e, props.id)}>
Remove
</a>
</div>
) : (
""
)}
</div>
);
};
const AddMore = (props) => {
function addMore() {
props.fieldsLengthChanger(props.fields.length + 1);
props.fieldsChanger([
...props.fields,
//{ id: props.fields.length + 1, name: "", phone: "", age: "" }
{ id: new Date().getTime(), name: "", phone: "", age: "" }
]);
}
return (
<div className="add__more">
<a href="#" onClick={(e) => addMore()}>
Add element
</a>
</div>
);
};
I have the next state:
const [social_networks, setSocial_networks] = useState([
{
social_account_type: "personal",
social_network: "linkedin",
handle: "",
content: ""
},
{
social_account_type: "company",
social_network: "twitter",
handle: "",
content: ""
},
{
social_account_type: "personal",
social_network: "webpage",
handle: "",
content: ""
}
])
In the parent component I declare the function:
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...social_networks];
list[index][name] = value;
setSocial_networks(list);
};
Set this to the children in the next code:
social_networks.map((social_network, idx) => {
if (social_network.social_account_type == "personal") return <div key={idx}><AccountsPill handle={social_network.handle} social={social_network.social_network} content={social_network.handle} index={idx} handleInputChange={handleInputChange} /> </div>
})
And into my child component I have the next code:
<div className="row m-0">
<div className="svg-container col-md-1">
<BrowserIcon color="#868E96" />
</div>
<input type="text" className="col-md-11 set-account-input" placeholder=
{"www."+props.social+".com"} name="handle" id="handle" defaultValue={props.handle}
onChange={e => props.handleInputChange(e, props.index)} />
</div>
<div className="row m-0">
<div className="svg-container col-md-1">
<AtIcon color="#868E96" />
</div>
<input type="text" className="col-md-11 set-account-input" placeholder="MyUsername"
name="content" id="content" defaultValue={props.content} onChange={e =>
props.handleInputChange(e, props.index)} />
</div>
The page show me like that:
after rendering frontpage
When I change the input.Content works fine:
input.name=content change
But, if I change the input.name=handle , change the other input too:
input.name=handle change
I tried to make two differents handleChange functions, change the props.name, add the props.id, but does'nt works yet.
You passed wrong content props to your AccountsPill component, it should be
<AccountsPill
handle={social_network.handle}
social={social_network.social_network}
content={social_network.content}
index={idx}
handleInputChange={handleInputChange}
/>
I think your problem is that const list = [...social_networks]; shallow copies the state array, so it's really just an array of the original state objects. Try instead:
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = social_networks.map((social, i)=>{
if(index === i){
return {...social, [name]: value}
}
return {...social}
})
setSocial_networks(list);
};
I'm trying to make a to-do list in React which I can edit and delete the to-dos. I'm trying to use this function to delete a to-do, which worked on a past project.
function deleteTodo(id) {
const updatedTodos = [...values].filter((values) => values.id !== id);
setValues(updatedTodos);}
But I'm trying to use this code on this new project and I get the error 'values is not iterable'. I imagine its because the 'values' is an object, not an array, as you can see below...
const [values, setValues] = useState({
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
});
What is the best way I can delete to-dos like this? This is my code:
export function App() {
const [values, setValues] = useState({
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
});
const [atividades, setAtividades] = useState([]);
function handleChange(event) {
setValues({
...values,
[event.target.name]: event.target.value,
[event.target.descricao]: event.target.descricao,
});
}
function handleSubmit(event) {
event.preventDefault();
setAtividades([
...atividades,
{
id: values.id,
nome: values.atividade,
descricao: values.descricao,
checkTrabalho: values.trabalho,
checkPessoal: values.pessoal,
},
]);
}
function deleteTodo(id) {
const updatedTodos = [...values].filter((values) => values.id !== id);
setValues(updatedTodos);
}
return (
// ==========================================================================================
<div className="App">
<form onSubmit={handleSubmit}>
<input
name="atividade"
type="text"
value={values.atividade}
onChange={handleChange}
/>
<input
maxLength="100"
name="descricao"
type="text"
value={values.descricao}
onChange={handleChange}
/>
<br />
<input
name="trabalho"
id="trabalho"
type="radio"
checked={values.trabalho}
onChange={() => {
setValues({ ...values, pessoal: false, trabalho: true });
}}
/>
<label htmlFor="trabalho">Trabalho</label>
<input
name="pessoal"
id="pessoal"
type="radio"
checked={values.pessoal}
onChange={() => {
setValues({ ...values, trabalho: false, pessoal: true });
}}
/>
<label htmlFor="pessoal">Pessoal</label>
<button type="submit">Criar</button>
</form>
{/* ========================================================================================== */}
{/* ========================================================================================== */}
<div key={values.id}>
<div>
<h1>Trabalhos</h1>
{atividades.map(
(atividade) =>
atividade.checkTrabalho === true && (
<div>
Todo: {atividade.nome} <br />
descricao: {atividade.descricao}
<button onClick={deleteTodo}>deletar</button>
</div>
)
)}
</div>
<div key={values.id}>
<h1>Pessoal</h1>
{atividades.map(
(atividade) =>
atividade.checkPessoal === true && (
<div>
Todo: {atividade.nome} <br />
descricao: {atividade.descricao}
<button onClick={deleteTodo}>deletar</button>
</div>
)
)}
</div>
</div>
</div>
// ==================================================================================================
);
}
It doesn't work because you have created an object instead of array of objects. If you want to store multiple todo objects you should create it like:
const [values, setValues] = useState([
{
id: Date.now(),
descricao: "",
atividade: "",
trabalho: true,
pessoal: false,
}
]);
Then you can use filter to remove items just like you are doing right now.
Or assuming you are storing your todos in atividades, then you should update that array of object instead of values.
const updatedTodos = [...atividades].filter((todo) => todo.id !== id);
I have a button that does a calculation based on the numbers provided by the user and populates the result to the page. That same button also saves the product to a database while also visually populating the page with those same products from the database. So basically it shows the total and the previous totals from whatever the user input into the fields. My issue is that I want to include error handling so that if the number is either 0 or NaN because they didn't fill all the forms, it would throw an alert and also not push the data to the database. This was my attempt and it does not work, if the result is NaN it still displays it and pushes the NaN value to the database, how do I achieve this? Attempt is bellow:
handleClick = (event, billInfo) => {
event.preventDefault();
const dbRef = firebase.database().ref();
const result =
(billInfo.amount / billInfo.group) * (billInfo.tip / 100 + 1);
// ATTEMPT START ======
result === NaN
? alert("Please fill all forms!")
: dbRef.push({
result: result.toFixed(2),
name: billInfo.name,
});
// ATTEMPT END ======
this.setState({
total: result.toFixed(2),
});
This is the form component with the button that links to the main app.js:
class inputForm extends Component {
constructor() {
super();
this.state = {
name: "",
group: "",
amount: "",
tip: "",
};
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
});
};
clearForm = (event) => {
event.preventDefault();
this.setState({
name: "",
group: "",
amount: "",
tip: "",
});
};
render() {
return (
<form className="tipApp">
<div>
<label htmlFor="groupName">
<i class="fas fa-id-card"></i>Group Name?
</label>
<input
onChange={this.handleChange}
type="text"
id="groupName"
value={this.state.name}
placeholder="Name your group"
name="name"
/>
</div>
<div>
<label htmlFor="groupSize">
<i class="fas fa-users"></i>How many?
</label>
<input
onChange={this.handleChange}
type="number"
id="groupSize"
value={this.state.group}
min="1"
step="1"
placeholder="Add the group size"
name="group"
required
/>
</div>
<div>
<label htmlFor="billAmount">
<i class="fas fa-receipt"></i>How much?
</label>
<input
onChange={this.handleChange}
type="number"
id="billAmount"
value={this.state.amount}
min="0"
step=".01"
placeholder="Add the bill total"
name="amount"
required
/>
</div>
<div>
<label htmlFor="tipAmount">
<i class="fas fa-heart"></i>Tip?
</label>
<input
onChange={this.handleChange}
type="number"
id="tipAmount"
value={this.state.tip}
min="10"
step="5"
placeholder="Add the tip %"
name="tip"
/>
</div>
<button onClick={(event) => this.props.getTotal(event, this.state)}>
Split it!
</button>
<button onClick={this.clearForm}>Reset</button>
</form>
);
}
}
This is the full app.js main file that I attempted to error handle on:
class App extends Component {
constructor() {
super();
this.state = {
bills: [],
total: 0,
};
}
componentDidMount() {
const dbRef = firebase.database().ref();
// Event listener that watches the database
dbRef.on("value", (snapshot) => {
const firebaseData = snapshot.val();
const billData = [];
for (const bill in firebaseData) {
billData.push({
id: bill,
name: firebaseData[bill].name,
value: firebaseData[bill].result,
});
}
this.setState({
bills: billData,
});
});
}
handleClick = (event, billInfo) => {
event.preventDefault();
const dbRef = firebase.database().ref();
const result =
(billInfo.amount / billInfo.group) * (billInfo.tip / 100 + 1);
result === NaN
? alert("Please fill all forms!")
: dbRef.push({
result: result.toFixed(2),
name: billInfo.name,
});
this.setState({
total: result.toFixed(2),
});
};
deleteBill = (billId) => {
const dbRef = firebase.database().ref();
dbRef.child(billId).remove();
};
render() {
return (
<div className="wrapper">
<h1 className="logoName">Spl|tr</h1>
<h3>Bill Splitting App</h3>
<Form getTotal={this.handleClick} />
<h2>${this.state.total}</h2>
<h3>Previous Bills</h3>
<Bills receipts={this.state.bills} delete={this.deleteBill} />
</div>
);
}
}
And this is the Bills.js component that populated the page with the previous items below the form on button click:
const Bills = (props) => {
return (
<ul>
{props.receipts.map((bill) => {
return (
<li key={bill.id}>
<p>{bill.name}</p>
<p>${bill.value}</p>
<button onClick={() => props.delete(bill.id)}>
<i class="fas fa-times-circle"></i>
</button>
</li>
);
})}
</ul>
);
};
Your handler updates state regardless of valid result or not. A small refactor should allow either a valid result and push to DB and update state OR an invalid result and alert. Both 0 and NaN are falsey values, so you can simply check if result is truthy/falsey.
handleClick = (event, billInfo) => {
event.preventDefault();
const dbRef = firebase.database().ref();
const result =
(billInfo.amount / billInfo.group) * (billInfo.tip / 100 + 1);
if (result) {
dbRef.push({
result: result.toFixed(2),
name: billInfo.name,
});
this.setState({
total: result.toFixed(2),
});
} else {
alert("Please fill all forms!")
}
};