Button Breaks React Form - javascript

I want to add button to my form to dynamically add inputs. Yet I found that, if i added a button to my form that simply logs to the console, (and when I try to add inputs) it logs and then the form breaks. The front end window of my Electron app crashes (doesn't exit but turns grey) and automatically restarts on the same page without the dialog open that contains the form.
Here is a snippet of my form code:
TaskCreation.js
return (
<div className="modal-body">
{values.products.map((product, i) => {
return(
<div key={i}>
<Form.Row>
<Form.Group as={Col} controlId={"keywords-" + i}>
<Form.Label>Keywords (e.g. '+box +logo +tee')</Form.Label>
<Form.Control
value={product.keywords.join(' ')}
onChange={handleChange}
>
</Form.Control>
</Form.Group>
</Form.Row>
...
<div style={{ marginTop:'10px' }}>
<button onClick={() => console.log(123)}>Add Product</button> // this breaks when clicked
</div>
...
);
Any help is welcome, let me know what other information I should provide.

I think that the button activates the 'submit' from form. So, you can try somethings. Add the atribute type="button" to your button, and/or use ".preventDefault()".
const handleButton = (event) => {
event.preventDefault()
console.log(123)
}
<div style={{ marginTop:'10px' }}>
<button type="button" onClick={handleButton}>Add Product</button>
</div>

Related

How do I make React onClick calls only affect a single item, instead of all?

I have an array of objects, and for each one I .map it into a component called Card.js. Each card has an 'edit' button, and I have an edit form which I want to appear ONLY for the card on which I clicked the button.
At the moment, whatever I try to do to pass an id into the Editform.js component, it still makes the form appear for all of the card components.
Here's the current component I call which is meant to render just form for the clicked button. I pass in all of the cards in the 'cards' array, and what I believe is the id of the current .map object from the calling function:
function Editform({ cards, setCards, id }) {
const thisCard = cards.filter((card) => card.id === id)[0];
const editThisCard = thisCard.id === id; // trying to match id of passed card to correct card in 'cards' array.
console.log(editThisCard);
return (
<>
{editThisCard && ( // should only render if editThisCard is true.
<div className="form">
<p>Name of game:</p>
<input type="text" value={thisCard.gamename}></input>
<p>Max players: </p>
<input type="text" value={thisCard.maxplayers}></input>
<p>Free spaces: </p>
<input type="text" value={thisCard.freespaces}></input>
<p>Table #: </p>
<input type="text" value={thisCard.tablenum}></input>
<p></p>
<button type="button" className="playbutton">
Save changes
</button>
</div>
)}
</>
);
}
export default Editform;
edit: apologies, I forgot to paste in the other code. Here it is. Note that I'm just hardcoding in a couple of cards for now:
import React from "react";
import ReactFitText from "react-fittext";
import Editform from "./Editform";
function Displaycards({ lastid }) {
const [cards, setCards] = React.useState([
{
id: 1,
gamename: "El Dorado",
maxplayers: 4,
freespaces: 1,
tablenum: 5,
},
{
id: 2,
gamename: "Ticket to Ride",
maxplayers: 4,
freespaces: 2,
tablenum: 3,
},
]); // using the React state for the cards array
const [showForm, setShowForm] = React.useState((false);
return (
<div className="cardwrapper">
{cards.map(({ id, gamename, maxplayers, freespaces, tablenum }) => {
return (
<div key={id}>
<div>
<div className="card">
<ReactFitText compressor={0.8}>
<div className="gamename">{gamename}</div>
</ReactFitText>
<div className="details">
<p>Setup for: </p>
<p className="bignumbers">{maxplayers}</p>
</div>
<div className="details">
<p>Spaces free:</p>
<p className="bignumbers">{freespaces}</p>
</div>
<div className="details">
<p>Table #</p>
<p className="bignumbers">{tablenum}</p>
</div>
<button type="button" className="playbutton">
I want to play
</button>
<br />
</div>
<div className="editbuttons">
<button
type="button"
className="editbutton"
onClick={() => setShowForm(!showForm)}
>
Edit
</button>
<button type="button" className="delbutton">
X
</button>
</div>
{showForm && (
<div>
<Editform
cards={cards}
setCards={setCards}
id={id}
/>
</div>
)}
</div>
</div>
);
})}
</div>
);
}
export default Displaycards;
I feel like I'm missing something obvious, but I can't get my head around what it is. The current iteration of it is here - https://github.com/TSDAdam/lfp/tree/usestate-trial - and it was created with create-react-app .
It sounds like you have one state controlling all of the Cards. You haven't shown the Card component yet however. Have every Card control its own state, so when the edit button bound to the card is clicked, it only applies to that one card. If you show us more code we can narrow it down, but this is most likely the gist of your problem.
The problem is that the EditForm is inside the map function, so for every item in your cards array, a separate EditForm is rendered with the corresponding values, and all these EditForms get shown/hidden based on the same boolean in your state.
The solution is to move the EditForm outside the map function, and create a new state object that tracks an "active" card, from where the single EditForm could take its values.
This of course won't work if you want to render the EditForm in a position relative to the "active" card.
[Edit]
Okay, I ended my answer with a caveat, but I should add a solution for that as well, since it isn't very complicated.
If you want to render an EditForm below the selected card, for example, the approach would be to keep it inside the map function as it is now, and change the boolean state variable showForm into one that accepts a string/number (depending on what you use as the identifier for each card). And then use this state variable to determine which form shows at any given time.
const [showForm, setShowForm] = React.useState("");
{cards.map(({ id, gamename, maxplayers, freespaces, tablenum }) => {
return (
<div key={id}>
// Rest of the JSX
<div className="editbuttons">
<button
type="button"
className="editbutton"
onClick={() => setShowForm(id)}
>
Edit
</button>
<button type="button" className="delbutton">
X
</button>
</div>
{showForm == id && (
<div>
<Editform
cards={cards}
setCards={setCards}
id={id}
/>
</div>
)}
</div>
</div>
);
})}

Javascript attribute - onClick and onClickOverThere for dropDown

I have reactJs app and I have made a custom dropDown with a div that I set an onClick attribute to open dropDown and close it.
but also I want to close it when the user clicks to another part of the site.
<div
onClick={() => setNoneQuote(noneQuote ? false : true)}
className="selected-drop-down"
>
<span className="dropDownText">{selectedQuoteCurrency}</span>
<img
className="dropDownIcon"
src={require("../assets/image/arrow/dropDown-Arrow.png")}
width="15px"
alt="arrow"
/>
</div>
I try onMouseDown instead of onClick according to this answer ==> stackoverflow ,but I don't know why it doesn't work for me :(
const dropdownElement= document.querySelector("choose an id or a specific class")
window.onclick = function(event) {
if (event.target !== dropdownElement) {
dropdownElement.style.display = "none"; // or any other function that you want to call
}
}
this may not be the exact code to fix your problem but you can use the logic
You could do it in different ways, but I will show you solution that I used in one of my projects.
So, I used <header> and <main> tags where I had all my Components. And in those tags i used eventListener with a callback function, like this:
<header onClick={handleClick}>
<Nav />
<Greeting />
<Sidebar />
</header>
and
<main onClick={handleClick}>
<Description />
<Icons />
<Prices />
<Gallery />
<NecessaryInfo />
<Location />
<GalleryModal />
<ConditionsModal />
</main>
In a callback function handleClick I checked where user could click and to do that I used next logic of pure JS:
!e.target.classList.contains("menu-open") &&
!e.target.classList.contains("menu-links") &&
!e.target.parentElement.classList.contains("menu-links") &&
!e.target.parentElement.parentElement.classList.contains("menu-links") &&
closeSidebar();
Function closeSidebar() is simple:
const closeSidebar = () => {
setIsSidebarOpen(false)};
In your code instead of using setNoneQuote(noneQuote ? false : true) you could also use: setNoneQuote(!noneQuote). Exclamation mark before value always will change it to the oposite.

Passing data to Reactstrap modal

I have some categories in my application. For each category, I'd like to add some images. So I added one Modal in reactstrap to show a modal window to add some files and descriptions. So far everything is Good. But the problem comes when I click on the launch modal button (add the file here), I'm unable to pass the id to the Modal. I tried to set a value to a variable using the useState hook but it will get a value only after the modal is closed.
My initials states like
const [categories, setCategories] = useState([])
const [categoryId, setCategoryId] = useState()
const [modal, setModal] = useState(false)
const toggle = (id) => {
setCategoryId(id)
setModal(!modal);
}
Then my code for showing categories
{ categories.map( cat => (
<div className="card border-0 rounded-0 img-thumbnail">
<img src={'assets/images/image_upload.jpg'} value={t._id} className="img-thumbnail" onClick={()=>toggle(cat._id)} alt="..."/>
</div>
)) }
<Modal
isOpen={modal}
toggle={toggle}
centered={true}
>
<ModalBody>
<div className="p-2">
<form>
<div className="form-group">
<input type="file" className="form-control-file" id="exampleFormControlFile1"/>
</div>
<div className="form-group">
<input type="text" className="form-control" id="exampleInputPassword1" placeholder="Caption Title"/>
</div>
<div className="form-group">
<textarea className="form-control" placeholder="Description" id="exampleFormControlTextarea1" rows="3"></textarea>
</div>
<button type="submit" className="btn btn-form">Upload</button>
<span onClick={toggle} style={{cursor: 'pointer'}} className="ml-2 btn btn-form-outline">Cancel</span>
</form>
</div>
</ModalBody>
</Modal>
I'm getting the category id only after the modal is closed. How can I set the value along with modal window????
The problem here is that toggle is also getting invoked when the modal gets opened & closed. You could have the toggle function specifically just for the setting of the state if the modal should be closed or open and have the setting of the id in a separate call.
const toggle = () => {
// setCategoryId(id) // remove this
setModal(!modal);
}
<img onClick={()=>{toggle(); setCategoryId(cat._id)}} />

Why a canvas update reload the page ? React

I would like to implement a signature system inside my form, I use "react-signature-canvas" for this.
Unfortunately, when I try to use it some problem appears.
My main problem is the reloading my page. Usually in React When you update something ,just the render changing without reloading. But here it does ... Is it linked to the canvas system ? I don't want the reload of my page... It's so ugly for a PWA.
For example when I click on "clear" or "trim" the page reload.
This is my code: I removed some parts of it to be clearer
const Consultation4 = () => {
const sigPad = {}
const [trimmedDataUrl,setTrimmedDataUrl] = useState(null);
const clear = () => sigPad.clear();
const trim = () => {
setTrimmedDataUrl(sigPad.getTrimmedCanvas().toDataURL('image/png'));
}
...
(In the render)
<FormGroup tag="fieldset" className="form-group">
<div className="container">
<div className="row">
<div className="col-4 margin-left--12px">
<legend>Please sign here:</legend>
</div>
<div className="col-4 margin-button-clear">
<button onClick={clear} className="btn btn-outline-secondary btn-sm">Clear</button>
</div>
<div className="col-4 margin-button-clear">
<button onClick={trim} className="btn btn-outline-secondary btn-sm">Trim</button>
</div>
</div>
</div>
<div className='border_grey'>
<SignaturePad ref={sigPad} />
</div>
</FormGroup>
{trimmedDataUrl ? <img src={trimmedDataUrl}/> : null}
}
Could you help me please ? Thanks in advance
PS: I have a second little problem, when I try to use sigPad.isEmpty() the compilator said "this method does not exist". But it exist in the doc of signature-pad-canvas (https://www.npmjs.com/package/react-signature-canvas).
The default type of button elements issubmit. Try button tags like
<button type="button" ....
within the form to stop submission from them. Text input elements may also need attention to stop pressing enter from submitting the form as well.

what is the difference between looping the div and making it as separate component and looping?

I need to know what's the difference between this code
{this.state.products &&
this.state.products.map((product, index) => (
<div key={index}>
<Subd
name={product.name}
price={product.price}
info={product.info}
image={product.image}
handleShow={this.showProduct}
handleTotal={this.calculateTotal}
/>
</div>
))}
and looping the div inside <Sudb/> component like
this.state.products &&
this.state.products.map((product, index) => (
<div className="row form-group">
<div className="col-sm-2">
<img
className="card-img-right flex-auto d-none d-lg-block"
alt="Thumbnail+{index}"
src={product.image}
/>
</div>
<div className="col-sm-6">
<h4>
{product.name}: ${product.price}
</h4>
<button className="btn btn-outline-primary" onClick={this.showInfo}>
show info
</button>
</div>
<div className="col-sm-4 text-right">
qty: {this.state.qty}
<br />
<button className="btn btn-outline-primary" onClick={this.add}>
{" "}
+1{" "}
</button>
<button
className="btn btn-outline-primary"
onClick={this.subtract}
disabled={this.state.qty < 1}
>
{" "}
-1{" "}
</button>
</div>
</div>`
in the above case if I update the quantity its affecting all the three objects but in the first case it's working fine. Can anyone explain what's the exact difference between both? Sandbox link Sandbox
In the second code I have made <Subd/> component code directly
The problem from what i can see in the code sample provided with the second sample provided is the following.
You are using the same piece of state for all products. so when you update this.state.qty for one row it updates all of them simultaneously.
The reason it does work when you separate it is because then each row has its own state that you can store individual quantities in.
If you did feel the need to not seperate out that code you could create an array on state for each of the qty's. and change up the subtract method to accept an index that it can use to access a certain rows qty state on the qty state array.
To pass in parameters to onclick you have to wrap it in a arrow function so it does not run automatically when the page mounts like so
onClick={() => {someFunction(param)}

Categories