I have the next form component in my application:
function App() {
const [state, setState] = useState(false)
const { register, control, handleSubmit, resetField, watch } = useForm();
const [checkedItems, setCheckedItems] = useState([]);
useEffect(()=> {
setCheckedItems(watch('test'))
},[watch('test')])
const onSubmit = (data) => alert(JSON.stringify(data, null, 4));
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h1>Field Array </h1>
<p>The following demo allow you to delete, append, prepend items</p>
<span className="counter">Render Count: {renderCount}</span>
<ul>
{[{value: 1}, {value:2}].map((item, index) => {
return (
<li key={item.userId}>
<Controller
render={({ field }) => <input type='checkbox' {...field} />}
name={`test[${index}]`}
control={control}
/>
</li>
);
})}
</ul>
<input onChange={e => setState(e.target.checked)} type='checkbox' />
{state && <input {...register(`test[${checkedItems.length}]`)}/>}
<input type="submit" />
</form>
);
}
If check this input:
<input onChange={e => setState(e.target.checked)} type='checkbox' />
I display an input where i can add data. Taking into account this scenario:
i click the checkbox, add some data, click again check box, the input dissapear, click again and add data and click submit, then as response i get a lot of data, but i need only the last value; Question: How to fix my issue?
demo: https://codesandbox.io/s/64182981-how-to-preserve-fields-in-react-hook-form-fieldarray-forked-qw18xs
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.
New to react and currently working on a project with a backend.
Everything functions correctly apart from targeting the value of user selection.
basically whenever a user enters a number the setId is saved properly to the const with no problems while using the onChange method.
this method would render my page every change on text.
I am trying to save the Id only when the user clicks the button. however,
event.target.value does not work with onClick.
I tried using event.currentTarget.value and this does not seem to work.
Code:
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
Handle Submit:
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
is there a way of doing this with onclick? since I wouldn't like my component to render on every typo and only once a user has clicked the button.
Componenet:
interface GetOneCompanyProps {
company: CompanyModel;
}
interface RouteParam {
id: any;
}
interface CompanyById extends RouteComponentProps<RouteParam> {
}
function GetOneCompany(): JSX.Element {
const [id, setId] = useState('4');
const [company, setCompany] = useState<any>('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(id)
}
async function send() {
try {
const response = await axios.get<CompanyModel>(globals.adminUrls.getOneCompany + id)
store.dispatch(oneCompanyAction(response.data));
console.log(response);
const company = response.data;
setCompany(company)
} catch (err) {
notify.error(err);
}
}
useEffect(() => {
send();
}, [id]);
return (
<div className="getOneCompany">
<h1>hi </h1>
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input value={id} onChange={(e) => setId(e.target.value)} type="number" />
{/* <button value={id} type="button" onClick={(e) => setId(e.currentTarget.value)}>Search</button> */}
</form>
<div className="top">
</div>
<br/>
Company: {id}
<br/>
Client Type: {company.clientType}
<br/>
Company Name: {company.name}
<br/>
Email Adress: {company.email}
<br/>
</div>
);
}
export default GetOneCompany;
Hope I am clear on this.
Thanks.
You can turn your input from being a controlled input to an uncontrolled input, and make use of the useRef hook. Basically, remove most of your attributes from the input element, and grab the current value of the input form on click of the button. From there, you can do whatever you want with the input value.
const inputRef = useRef()
...other code
<form onSubmit={handleSubmit}>
<label>Company ID</label>
<input type="number" ref={inputRef} />
<button value={id} type="button" onClick={() => console.log(inputRef.current.value)}>Search</button>
</form>
...other code
I'm afraid to say that here onChange is mandatory as we also are interested in the value which we set by setId. onClick can't be used as we can't set the value in the input.
Hope I'm clear.
Thankyou!
Im just wondering if anyone could point out where im going wrong with my code.
Im relativly new to react so began with a simple todo list.
I then edited this to allow for various other forms such as menu, profile etc.
Below is the code attached for the menu section.
My back end works if I use postmaster which leads me to believe its my front end, and specifically my useState.
I can call the data and view it within my modal and it appears, however, I cant seem to edit the specific data within the form field and/or post it to my database.
Any help would be greatly appreciated.
Ive attached my code below.
import React, { Fragment, useState } from "react";
const EditMenu = ({ menu }) => {
//editText function
const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category);
const { title, category, price, description } = inputs;
const onChange = e =>
setInputs({ ...inputs, [e.target.name]: e.target.value });
const editMenuItem = async (item_id) => {
try {
const body = { title, category, price, description };
const res = await fetch(`http://localhost:5000/menu/${item_id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
window.location = "/admin";
} catch (error) {
console.error(error.message);
}
};
return (
<Fragment>
<button type="button" className="btn btn-warning" data-toggle="modal" data-target={`#id${menu.item_id}`}>Edit</button>
{/*id = "id21"*/}
<div className="modal" id={`id${menu.item_id}`} onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Edit Menu</h4>
<button className="close btn-danger" data-dismiss="modal" onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>×</button>
</div>
<div className="modal-body">
<input type="text" name="title" placeholder="Title" className="form-control my-3" value={menu.item_title} onChange={e => onChange(e)} />
<input type="tel" name="price" placeholder="Price" className="form-control my-3" value={menu.item_price} onChange={e => onChange(e)} />
<input type="text" name="description" placeholder="Description" className="form-control my-3" value={menu.item_description} onChange={e => onChange(e)} />
<input type="text" name="category" placeholder="Category" className="form-control my-3" value={menu.item_category} onChange={e => onChange(e)} />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-warning" data-dismiss="modal" onClick={() => editMenuItem(menu.item_id)}>Edit</button>
<button type="button" className="btn btn-danger" data-dismiss="modal" onClick={() => setInputs(menu.item_title, menu.item_price, menu.item_description, menu.item_category)}>Close</button>
</div>
</div>
</div>
</div>
</Fragment>
);
};
Update,
Ive tried various suggested fixes using the below answers so far.
Both of these fixes allow the form fields to be editable, and the information within the form fields changes and thus within the state also however it is not sent to the database. Upon refresh of the page, the old information is pulled from the database.
Ive discovered that if I removed all of the form fields but one, it successfully updates AND sends to the database.
Title OR Description OR Price OR Category.
Checking the network tab within the browser whilst updating shows that for more than one input field, the put request fails and no information/payload is sent to the body within the request tab.
As a result, the database returns a NOT NULL error.
Based off Oliviers answer below, that setInput is only recognises one parameter, I can only imagine that this is what is breaking when there is more than one form field/input added. I unfortunatly dont know enough react to know if this is the case or not.
I see a problem in your state initialization => const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category); is not correct, useState take a single parameter, here you must build an object representing the inputs.
Here is a solution using a function to initialize the inputs state, to prevent computing the object each time the component is re-rendered
function buildInputs(menu) {
return {
title: menu.item_title,
category: menu.item_category,
price: menu.item_price,
description: menu.item_description
};
}
const EditMenu = ({ menu }) => {
//editText function
const [inputs, setInputs] = useState(() => buildInputs(menu));
const { title, category, price, description } = inputs;
// Needed if you want the inputs to be updtated when the menu property is updated
useEffect(() => setInputs(buildInputs(menu)), [menu]);
const onChange = e => setInputs({ ...inputs, [e.target.name]: e.target.value });
...
You must also change the input value to reflect the state variable :
<input type="text" name="title" placeholder="Title"
className="form-control my-3" value={title} onChange={onChange} />
You should set your state like this:
const [inputs, setInputs] = useState({
title: menu.item_title,
price: menu.item_price,
category: menu.item_category,
description: menu.item_description
});
also you need to change value attributes to be variables rather than setting them to the menu values, for example:
//code
<input name="title" value={inputs.title} onChange={onChange}/>
cause values inside inputs are changeable by your onChange method, on the other hand, values inside menu object will remain with the same values.
I eventually figured out the issue.
By splitting my setInput useState into seperate individual useStates, I was able to get it to work.
So my origional code of...
const EditMenu = ({ menu }) => {
const [inputs, setInputs] = useState(menu.item_title, menu.item_price, menu.item_description, menu.item_category);
const { title, category, price, description } = inputs;
changed to this.
const EditMenu = ({ menu }) => {
const [item_title, setTitle] = useState(menu.item_title);
const [item_price, setPrice] = useState(menu.item_price);
const [item_description, setDescription] = useState(menu.item_description);
and the onChange function and form input...
const onChange = e =>
setInputs({ ...inputs, [e.target.name]: e.target.value });
<input... onChange={e => onChange(e)} />
changed to this...
value={item_title} onChange={e => setTitle(e.target.value)} />
value={item_price} onChange={e => setPrice(e.target.value)} />
value={item_description} onChange={e => setDescription(e.target.value)} />
In the end, Oliviers reasoing was correct even if the soloution didnt work for me. That my setInput only allowed for one parameter. Splitting it up allowed me to pass the remaining parameters.
Thank you everyone for the help, hopefully this might help someone else some day too!
I have to show a list of defaultValues in the search list and when I click on any of those item then it should take me to that item's component but it's not going anywhere. It's only happening with the defaultValues because as soon as I start typing, then if I click on any search result then it takes me to the desired component. what is wrong with my code?
here's the code
const [search, setSearch] = useState("");
const [showDefaultValues, setShowDefaultValues] = useState(false);
const [defaultValues] = useState({
Mumbai: true
});
{!search.length && showDefaultValues ? (
<div className="result-box">
{data
.filter((item, idx) => defaultValues[item.district])
.map((dist, idx) => (
<div
key={idx}
className="search-result"
onClick={() => {
onResultClick(dist.district);
}}
>
{highlightedText(dist.district, search)}
</div>
))}
</div>
) : null}
Just change the codes at components/search/search.js line 39 to 49
<input
placeholder="Search for a District..."
type="text"
className="search-input"
value={search}
onChange={onSearchInputChange}
onFocus={() => {
toggleDefaultValues(true);
}}
onBlur={onBlurInput}
/>
To
<input
placeholder="Search for a District..."
type="text"
className="search-input"
value={search}
onChange={onSearchInputChange}
onFocus={() => {
toggleDefaultValues(true);
}}
/>
Or simply remove line 48
To compensate this, you can add below inside your useEffect (similar to componentDidMount)
document.addEventListener("mousedown", handleInputClickOutside);
add function handleInputClickOutside to set the state to false/hide
You forgot to implement the onClick logic on the default search result items and that's why the search results work fine, while the default search items do not.
Check this link to the working codesandbox.
All i did was invoke the same onResultClick function onClick of 'District' component.
<div
className="dist"
onClick={() => {
this.props.onResultClick(item.district);
}}
>
...
</div>
Hope this solves your problem.