React hook form Rendering too many times - reactjs

I am trying to do something like this.
When user Click on Edit Button then Form fields will appear and user can edit the data. otherwise user can only view the data.
I facing the trouble, when user don't want to edit the form and click on cancel button then page start rendering as many times as the page have total form fields.
On my original form I have 80+ form fields and when user click on cancel button page becomes very slow.
When I remove
ref={register}
from my page then form don't render to many times, but form does not submit with the data.
Is there any way to stop extra rendering?
Here is my code and logic.
Thanks for your attention.
import React, { useState } from "react";
import { useForm } from "react-hook-form";
function Test() {
const { register, handleSubmit } = useForm();
const [edit, setEdit] = useState(false);
const onSubmit = (data) => {
console.log(data);
};
return (
<div className="App">
<header className="App-header">
{console.log("I am redering.")}
<form onSubmit={handleSubmit(onSubmit)}>
<a
href=""
onClick={(e) => {
setEdit(!edit);
e.preventDefault();
}}
>
{edit ? "Cancel" : "Edit"}
</a>
{edit && (
<p>
<input
type="text"
name="email"
ref={register}
placeholder="Email"
/>
</p>
)}
{edit && (
<p>
<input
type="text"
name="firstname"
ref={register}
placeholder="First Name"
/>
</p>
)}
{edit && (
<p>
<input
type="text"
name="lastname"
ref={register}
placeholder="Last Name"
/>
</p>
)}
{edit && (
<>
<input
type="checkbox"
name="contact[]"
id="contact-one"
value="1"
ref={register}
/>
<label htmlFor="contact-one">One</label>
</>
)}
{edit && (
<>
<input
type="checkbox"
name="contact[]"
id="contact-two"
value="2"
ref={register}
/>
<label htmlFor="contact-two">Two</label>
</>
)}
{edit && <button type="submit">Submit</button>}
{edit === false && (
<>
<p>{`My First Name`}</p>
<p>{`My Last Name`}</p>
<p>{`My Email address`}</p>
<p>{`My Contacts`}</p>
</>
)}
</form>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default Test;

<form onSubmit={handleSubmit(onSubmit)}>
This is a function call, which means this gets called immediatelt the form is created on the DOM, your event listeners need to take a reference to the function which will then get called when the event occurs, to do that you wrap this function call in another anonymous function. Then the function will get called when the submit event occurs. So you need to do this
<form onSubmit={()=>{handleSubmit(onSubmit)}}>
Also since this is a submit event, you might want to stop the default behaviour which refreshes the page. Like this
<form onSubmit={(ev)=>{
ev.preventDefault()
handleSubmit(onSubmit)}}>
Update
I did not look at the JSX properly. There are a few things you are probably doing wrong
using ref to get input values rather than state
when taking in any kind of input you want to convert those fields into control components Officila docs on controlled components
I suggest you understand this and state first, that will help you a lot. React docs still uses class components in example code but simply use use state instead of this.state for state varibales

Related

Reactjs input tag causing the whole page to not load

So, I was learning React Hooks and everything was going fine until the tag was added as I normally would add it like this: , however, this caused the whole page to collapse but writing it in this way, or react usual way to witting tags made it work again. any explanation behind this?
import React from 'react'
import { useState } from 'react'
function CounterHook() {
const [count, Setcount] = useState(0)
let [text, set_text] = useState("This is a Test TEXT")
let [info , set_info] = useState({name:'', email:''})
return (
<div>
<h3>{count}</h3>
<button onClick={() => Setcount(count + 1)} className='btn btn-primary'> Click </button>
<h3> {text} </h3>
<button onClick={()=> set_text("The test Text has change nothing is the same anymore ")}
className='btn btn-success'> Change Me </button>
<br />
<br />
<form>
<input type="text" className={'form-control'} value={info.name}
onChange={ event => set_info({name: event.target.value})} /> Enter your Name
<input type={'text'} className={'form-control'} value={info.email}
onChange={ event => set_info({email: event.target.value})} /> Enter your Email
{/* COMMENTED OUT CODE */} {/* that part of the code made the whole page blank */}
{/* <input type="text" className={'form-control'} value={info.name}
onChange={ event => set_info({name: event.target.value})}> Enter your Name </input>
<input type={'text'} className={'form-control'} value={info.email}
onChange={ event => set_info({email: event.target.value})}> Enter your Email </input> */}
<h2> Name is: {info.name} </h2>
<h2> Email is : {info.email} </h2>
</form>
</div>
)
}
export default CounterHook
So one problem that immediately jumps out at me is that info is supposed to be an object with the shape: {name:'', email:''} but you are setting it to {name:''} or {email:''} which will cause the object to be missing one of the object props. You are then trying to reference both props in which one of them will be undefined depending on what input you type in. Try having a two separate states for each of the values like so:
const [name, setName] = useState('');
const [email, setEmail] = useState('');
Alternatively you could try in your onChange event something like this:
This is for the name input event handler
(event)=> set_info(previousState=> {name:event.target.value, email:previousState.email})
I haven't tested option 2 but in theory it should work. Hope this helps.

handleSubmit from formik is invoked whichever button I click

I want to implement a checking form for validity into the project for ordering food using formik but I have encountered some problems creating two buttons. Whichever button is clicked the handleSubmit is invoked. what can I do to solve this problem?
Function goBack only sets the state to false.
export default function Checkout(props) {
function handleSubmit(event) {
// event.preventDefault();
console.log("Hello");
}
return (
<Formik
initialValues={{ userName: "Hi", street: "", postalCode: "", city: "" }}
onSubmit={handleSubmit}
>
{(props) => (
<Form className={styles["form"]}>
<div className={styles["form-control"]}>
<div className={styles["form-control__input"]}>
<label htmlFor="userName">Your name</label>
<Field type="text" name="userName" id="userName"></Field>
</div>
<div className={styles["form-control__input"]}>
<label htmlFor="street">Street</label>
<Field type="text" name="street" id="street"></Field>
</div>
<div className={styles["form-control__input"]}>
<label htmlFor="postalCode">Postal code</label>
<Field type="text" name="postalCode" id="postalCode"></Field>
</div>
<div className={styles["form-control__input"]}>
<label htmlFor="city">City</label>
<Field type="text" name="city" id="city"></Field>
</div>
</div>
<div className={styles["form-actions"]}>
<CloseButton type="button" onClick={props.goBack}>Back</CloseButton>
<OrderButton type="submit">Confirm</OrderButton>
</div>
</Form>
)}
</Formik>
);
}
export default function CloseButton(props) {
return <button type={props.type} onClick={props.onClick} className={styles["close-button"]}>{props.children}</button>;
}
export default function OrderButton(props) {
return <button type={props.type} onClick={props.onClick} className={styles['order-button']}>{props.children}</button>
}
I wanted CloseButton to close the form and go back to the list of orders, but it only invokes handleSubmit created by Formik component instead of the function in the props. I have read the documentation but there is neither anything about creating formik with two buttons nor anything related to my problem.
Looks like in props.goBack you meant to reference the props for the component, but instead the props from within Formik are being used (as that is the closest block declaration of props). Since goBack is not defined on the Formik props, you're passing undefined as the onClick handler to the button.
The most straightforward way to fix this is to rename one of the props variables—I'd suggest naming the Formik ones to formikProps or something similar.
A better approach, in my opinion, would be to destructure the props (in both cases, though only one is necessary), like this:
export default function Checkout({ goBack }) {
// ...
return (
<Formik>
{(props) => (
// ...
<CloseButton type="button" onClick={goBack}>Back</CloseButton>
// ...
)}
</Formik>
)
}

onChange not getting called React

I'm trying to debug a react checkbox and the onChange event doesn't ever get called. I have added a console.log to test and this doesn't ever run. Here is the code for the checkbox. What is the issue?
return (
<div className="RampPane">
<div className="RampPane--content">
<p className="RampText">{transaction.merchant} </p>
<b>{moneyFormatter.format(transaction.amount)}</b>
<p className="RampText--hushed RampText--s">
{transaction.employee.firstName} {transaction.employee.lastName} - {transaction.date}
</p>
</div>
<InputCheckbox
id={transaction.id}
checked={approved}
disabled={loading}
onChange={async (newValue) => {
console.log("click")
await consumerSetTransactionApproval({
transactionId: transaction.id,
newValue,
})
setApproved(newValue)
}}
/>
</div>
)
Here is the InputCheckBox Component
return (
<div className="RampInputCheckbox--container" data-testid={inputId}>
<label
className={classNames("RampInputCheckbox--label", {
"RampInputCheckbox--label-checked": checked,
"RampInputCheckbox--label-disabled": disabled,
})}
/>
<input
id={inputId}
type="checkbox"
className="RampInputCheckbox--input"
checked={checked}
disabled={disabled}
onChange={() => onChange(!checked)}
/>
</div>
)
Use this for InputCheckBox Component
return (
<div className="RampInputCheckbox--container" data-testid={inputId}>
<label
className={classNames("RampInputCheckbox--label", {
"RampInputCheckbox--label-checked": checked,
"RampInputCheckbox--label-disabled": disabled,
})}
htmlFor={inputId}
/>
<input
id={inputId}
type="checkbox"
className="RampInputCheckbox--input"
checked={checked}
disabled={disabled}
onChange={() => onChange(!checked)}
/>
</div>
)
This works!
If the InputCheckBox is a custom built react component that renders an html input element try checking that the onChange handler is passed to the root component correctly
IF Not try including the component here to get a better insight

How to add no of fields dynamically by taking input from user react

I want to add the input no of fields given by the user when clicking on submit button.How to do this in react functional component.
screenshot:
I have an input field,when any user input on that field and submit,according to the input given by the user the no fields will be created.like in above screenshot if a user gives input 6 then 6 fields will be added
I am trying in this way,
import React, { useState } from 'react'
import cal from './image/bgimg.jpg'
function Home() {
const [state,setState]=useState({
semester:'',
credit:'',
sgpa:''
})
const [noOfSem,setNoOfSem]=useState()
const handleChange=(e)=>{
setState({...state,[e.target.name]:e.target.value})
}
const handleClick=()=>{
console.log('hyy',state.semester)
setNoOfSem([state.semester])
}
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img src={cal} alt="" className='imgcal img-fluid' />
</div>
<div className="col-md-6">
<div className="col-md">
<div className="form1">
<input type="number" value={state.semester} name='semester' onChange={handleChange} placeholder='Enter Total Semester' />
<button type="button" class="btn btn-success" onClick={handleClick}>Submit</button>
<div className="form2">
{noOfSem?.map((item,index)=>
<>
<input type="text" placeholder={`Enter your Semester ${index+1} credit`} key={index}/>
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default Home
thanks......
I think there's a few changes you can make to improve the code and get it working.
Firstly, I would avoid storing your number of semesters in both the state.semester and noOfSem state, as it means you have to update both of them to keep them in sync.
Given that your component only needs to know the number of semesters when the user presses Submit, you can remove the handleChangeCall and instead only access the value upon submit.
It is also good practice to make use of the <form onSubmit={}> and <input type='submit'> elements, to handle form submission. Instead of using the onClick event from a regular <button>. Some info here.
When using the form, you can then access the value of the semester input directly by storing a reference to it using useRef.
Then in order to iterate over the number of semester, you can construct a new Array to map over. One caveat here is that you have to call the array fill method.
See solution here:
import React, { useState, useRef } from "react";
function Home() {
const [state, setState] = useState({
semester: '',
credit: "",
sgpa: ""
});
const semesterInputRef = useRef();
const handleForm1Submit = (e) => {
e.preventDefault();
if (semesterInputRef?.current.value) {
setState({ ...state, semester: Number(semesterInputRef.current.value) });
}
};
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<div className="col-md">
<form className="form1" onSubmit={handleForm1Submit}>
<input
type="number"
name="semester"
ref={semesterInputRef}
placeholder="Enter Total Semester"
></input>
<input type="submit" className="btn btn-success"></input>
</form>
<form className="form2">
{state.semester &&
Array(state.semester).fill().map((_item, index) => (
<input
key={index}
type="text"
placeholder={`Enter your Semester ${index + 1} credit`}
></input>
))}
</form>
</div>
</div>
</div>
</div>
);
}
export default Home;
I've also created a code sandbox to show that this works as expected, here.

Dropdown option cannot be selected if I put the component inside my label

Why doesn't my Dropdown component work when I put it inside my <label> tag? The dropdown is displaying the options, but if you click on one, the selection is not working/displayed.
Any suggestions?
export default function App() {
return (
<div>
<div className="mt-10 mb-3 h-6 text-md uppercase font-bold>
People
</div>
<button type="button" onClick={addInvitee}>
+Add menu
</button>
<form onSubmit={handleSubmit}>
{invited.map(({ age, email, id, location, name }, index) => (
<div key={id}>
<div className="grid grid-cols-3 gap-5">
<label className="mr-3 h-6 text-md font-bold">
Names:
<input
type="text"
value={name}
placeholder="Names"
name="name"
onChange={updateInvitee(id)}
/>
</label>
//other inputs with the exact same pattern
<label>
Choice:
<Dropdown className="w-3/5" options={CHOICE} isMulti={false} />
</label>
...//other inputs
</form>
</div>
);
}
Dropdown.jsx
import React, { useState } from "react";
import Select from "react-select";
import Tag from "./Tag";
export default function Dropdown({
className,
style,
options,
styleSelect,
defaultValue,
isMulti = false
}) {
const [selected, setSelected] = useState(defaultValue);
const styles = {
select: {
width: "100%",
maxWidth: 200
}
};
return (
<div style={style}>
{selected && isMulti === false ? (
<Tag
selected={selected}
setSelected={setSelected}
styleSelect={styleSelect}
/>
) : (
<Select
className={className}
style={styles.select}
value={selected}
onChange={setSelected}
options={options}
isMulti={isMulti}
/>
)}
</div>
);
}
Here is my CodeSandbox
Firstly here's something important to note:
Based on this image, a console snippet from sandbox, it shows that the selection happens, but it gets cleared instantly.
What's the cause for this? Hmm.. let's take a look.
Consider this code snippet:
<label>
Press the text
<br /><br />
<button onClick="console.log('button-clicked')">Button</button>
</label>
Here, the <button> is placed inside a <label>. Do you notice, that when you click on press the text, the button's onClick gets triggered? ... But why?
Well, by default, a <label> is a focusable html element, and when focused, it triggers any form control element placed inside it.
So how does this relate to your code?
You have this line of code inside your <Tag> component onClick={() => setSelected(null)} and that's where the problem is. When you pick a selection, the selected gets updated and the component re-renders and displays your <Tag> component... but the event still bubbles up tree again until it reaches the <label> element. remember, at this point, it's no longer the <Select> component shown, but the <Tag> component. And guess what? the <label> gets focused and triggers the <button> inside your <Tag> component which clears (setSelected(null)) the selected state property. Once the selected is cleared, the <Dropdown> component re-renders and the <Select> component is displayed again.
This goes on and on and on as you try to select, then the process repeats.
So, from your code... Just remove this here onClick={() => setSelected(null)} and you'll see it will work. So you just need to work around it on how to clear the selected, but I have suggested a solution below.
The Solution
In your <Dropdown> component, we should try and prevent the event from bubbling. So all you need to do is add the following onClick={e=>e.preventDefault()} in your <div>
<div style={style} onClick={(e) => e.preventDefault()}>
{selected && isMulti === false ? (
<Tag
selected={selected}
setSelected={setSelected}
styleSelect={styleSelect}
/>
) : (
<Select
className={className}
style={styles.select}
value={selected}
onChange={setSelected}
options={options}
isMulti={isMulti}
/>
)}
</div>
Here's the original sandbox with the solution applied.

Resources