I have this button here
<button className={Classes.Button}
disabled={!isEnabled}
type="submit">
{buttonText}
</button>
which should be disabled or enable after checking the value of some of my inputs.
the conditions
const canBeSubmitted = () => {
return (
customerData.firstNameState.length > 0 && // TextInput
customerData.emailState.length > 0 && // TextInput
customerData.companeyState.length > 0 && // TextInput
customerData.selected.length > 0 && // Dropdown
customerData.agree === true // checkbox for terms
);
};
let isEnabled = canBeSubmitted();
BTW: The agree checkbox is checked by its handler and works fine.
The agree value is false in the state and the handler
const handleChange = (event) => {
const field = event.target.id;
if (field === "firstName") {
setFirstName({ firstName: event.target.value });
} else if (field === "email") {
setEmail({ email: event.target.value });
} else if (field === "country") {
setSelected({ country: event.target.value });
} else if (field === "agree") {
setAgree(!agree);
console.log(agree);
}
};
but always return false. what am I missing?
Please help me out
If I'm correct, your 'state' isn't changing because of how you're changing 'state variables' in handleChange fat arrow function.
I could be wrong depending on how your 'state' is structured.
I'm assuming your 'state' is structured like this.
const [firstName, setFirstName] = useState("");
const [email, setEmail] = useState("");
const [country, setCountry] = useState("");
const [agree, setAgree] = useState(false);
Fix your handleChange function.
// Commented out your possibly erroneous code.
const handleChange = (event) => {
const field = event.target.id;
if (field === "firstName") {
// Fix here.
// setFirstName({ firstName: event.target.value }); ❌
setFirstName(event.target.value); ✅
} else if (field === "email") {
// Fix here.
// setEmail({ email: event.target.value }); ❌
setEmail(event.target.value); ✅
} else if (field === "country") {
// Fix here.
// setSelected({ country: event.target.value }); ❌
setCountry(event.target.value); ✅
} else if (field === "agree") {
// Fix here.
// setAgree(!agree); ❌
setAgree(event.target.checked); ✅
console.log(agree);
}
};
You can then perform your validation like this:
const canBeSubmitted = () => {
return (
firstName.trim().length && // TextInput
email.trim().length && // TextInput
country.trim().length && // Dropdown
agree // checkbox for terms
);
};
It appears your also have a typo here for 'countryState':
customerData.companeyState.length > 0 && // TextInput
It looks like there is a full stop after && operator in your code.
customerData.selected.length > 0 &&. // Dropdown
Addendum
#Harry9345, you can as well get rid of the handleChange completely.
Full source code below. Demo: https://codesandbox.io/s/crimson-fog-vp19s?file=/src/App.js
import { useEffect, useState } from "react";
export default function App() {
const [firstName, setFirstName] = useState("");
const [email, setEmail] = useState("");
const [country, setCountry] = useState("");
const [agree, setAgree] = useState(false);
const canBeSubmitted = () => {
const isValid =
firstName.trim().length && // TextInput
email.trim().length && // TextInput
country.trim().length && // Dropdown
agree; // checkbox for terms
if (isValid) {
document.getElementById("submitButton").removeAttribute("disabled");
} else {
document.getElementById("submitButton").setAttribute("disabled", true);
}
console.log({ firstName, email, country, agree });
};
useEffect(() => canBeSubmitted());
return (
<div>
<form action="" method="post" id="form">
<label htmlFor="firstName">First name:</label>
<br />
<input
type="text"
id="firstName"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<br />
<label htmlFor="email">Email Address:</label>
<br />
<input
type="email"
id="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<br />
<label htmlFor="country">Choose a country:</label>
<br />
<select
id="country"
name="country"
value={country}
onChange={(e) => setCountry(e.target.value)}
>
<option value="">Select..</option>
<option value="1">USA</option>
<option value="2">Canada</option>
<option value="3">Algeria</option>
</select>
<br />
<input
type="checkbox"
name="agree"
id="agree"
onClick={(e) => setAgree(e.target.checked)}
/>
<label htmlFor="agree"> I agree.</label>
<br />
<button type="submit" id="submitButton">
Submit
</button>
</form>
</div>
);
}
You should use state instead of variable:
I added some example:
import { useState } from "react";
const App = () => {
const [isDisabled, setIsDisabled] = useState(true);
const [checked, setChecked] = useState(false);
const canBeSubmitted = () => {
return checked ? setIsDisabled(true) : setIsDisabled(false);
};
const onCheckboxClick = () => {
setChecked(!checked);
return canBeSubmitted();
};
return (
<div className="App">
<input type="checkbox" onClick={onCheckboxClick} />
<button type="submit" disabled={isDisabled}>
Submit
</button>
</div>
);
};
export default App;
codesandbox
Of course it's just a sample of code and not very efficient.
Related
I did CODESANDBOX with your code example: codesandbox.io/s/keen-lehmann-dtwz2y?file=/src/App.tsx, So you can see how it turned out! This select don't work when I want to change value. Please show me how to make the same select but working!
This my Select.tsx:
import styles from "./select.module.scss"
import { useForm } from "react-hook-form";
import { useRef, useState } from "react";
interface IFormInput {
sortType: string;
}
const Select = () => {
const { register, handleSubmit } = useForm<IFormInput>();
const [isOpened, setIsOpened] = useState(false);
const [checked, setChecked] = useState(true);
const dropdownRef = useRef(null);
const handleClick = (e:any) => {
setChecked(e.target.value);
e = true;
};
const onSubmit = (data: IFormInput) => {
console.log(data);
};
const handleOnClick = (event: any) => {
console.log("test");
setIsOpened(!isOpened);
event.stopPropagation();
event.preventDefault();
};
return (
<div className={styles.select}>
<p className={styles.pSelect}>Who are creating the ad?</p>
<div className={styles.body}>
<span
ref={dropdownRef}
onClick={handleOnClick}
className={isOpened ? "expanded dropdown-el" : "dropdown-el"}
>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="radio"
name="radio"
value="Freelancer"
id="sort-relevance"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
<label htmlFor="sort-relevance">Freelancer</label>
<input
type="radio"
name="radio"
value="Client"
id="sort-best"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
<label htmlFor="sort-best">Client</label>
</form>
</span>
</div>
</div>
);
}
export default Select
export default function App() {
const { register, handleSubmit } = useForm<IFormInput>();
const [isOpened, setIsOpened] = useState(false);
const dropdownRef = useRef(null);
const [checked, setChecked] = useState("Client"); // Set first initial value for select
const handleClick = (e) => {
setChecked(e.target.value);
e.target.defaultChecked = true;
};
const onSubmit = (data: IFormInput) => {
console.log(data);
};
const handleOnClick = (event: any) => {
console.log("test");
setIsOpened(!isOpened);
event.stopPropagation();
event.preventDefault();
};
return (
<div>
<span
ref={dropdownRef}
onClick={handleOnClick}
className={isOpened ? "expanded dropdown-el" : "dropdown-el"}
>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="radio"
name="radio"
id="sort-relevance"
checked={checked === "Freelancer"}
onClick={(e) => handleClick(e)}
/>
<label
htmlFor="sort-relevance"
onClick={() => setChecked("Freelancer")}
>
Freelancer
</label>
<input
type="radio"
name="radio"
id="sort-best"
checked={checked === "Client"}
onClick={(e) => handleClick(e)}
/>
<label htmlFor="sort-best" onClick={() => setChecked("Client")}>
Client
</label>
</form>
</span>
</div>
);
}
There is a component:
import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
import { NavLink } from "react-router-dom";
const EditIntern = () => {
const { id } = useParams();
const [intern, setIntern] = useState([]);
const [name, inputName] = useState("");
const [email, inputEmail] = useState("");
const [start, inputStart] = useState("");
const [end, inputEnd] = useState("");
const [errorNameEmpty, isErrorNameEmpty] = useState(true);
const [errorEmailEmpty, isErrorEmailEmpty] = useState(true);
const [errorEmailValid, iserrorEmailValid] = useState(false);
const [errorStartEmpty, isErrorStartEmpty] = useState(true);
const [errorEndEmpty, isErrorEndEmpty] = useState(true);
const validEmail = new RegExp(
/(\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)/gm
);
const dateValidate = () => {
if (start.value > end.value) {
console.log("Start > end");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
} else {
console.log("Ok");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
}
};
useEffect(() => {
const fetchIntern = async () => {
const response = await fetch(`http://localhost:3001/interns/${id}`);
const intern = await response.json();
setIntern(intern);
};
fetchIntern();
console.log(`I want to get intern with id: ${id}!`);
}, [id]);
return (
<div>
<NavLink to="/">Back to list </NavLink>
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
} else {
isErrorNameEmpty(false);
}
inputName(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
}
}}
/>
{errorNameEmpty ? <span>Name can't be empty</span> : <></>}
<label>Email</label>
<input
type="text"
name="email"
value={email}
onChange={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
} else if (!validEmail.test(e.target.value)) {
iserrorEmailValid(true);
isErrorEmailEmpty(false);
} else {
iserrorEmailValid(false);
isErrorEmailEmpty(false);
}
inputEmail(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
}
}}
/>
{errorEmailEmpty ? <span>Email can't be empty</span> : <></>}
{errorEmailValid ? <span>Example: email#gmail.com</span> : <></>}
<label>Start date</label>
<input
type="date"
name="email"
value={start}
onChange={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
} else {
isErrorStartEmpty(false);
}
inputStart(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
}
}}
/>
{errorStartEmpty ? <span>Start date can't be empty</span> : <></>}
<label>End date</label>
<input
type="date"
name="email"
value={end}
onChange={(e) => {
inputEnd(e.target.value);
if (e.target.value === "") {
isErrorEndEmpty(true);
} else {
isErrorEndEmpty(false);
}
dateValidate();
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEndEmpty(true);
}
}}
/>
{errorEndEmpty ? <span>End date can't be empty</span> : <></>}
<input type="submit" value="Submit" />
</form>
<h2>{intern.name}</h2>
<h2>{intern.email}</h2>
<h2>{intern.internshipStart}</h2>
<h2>{intern.internshipEnd}</h2>
</div>
);
};
export default EditIntern;
It has two inputs of type date. The task is to check that the start date is not greater than the end date.
I created a dateValidate function for this (which doesn't work as it should).
How can you solve this problem? (Perhaps my approach to form validation is generally not correct, I’ll be happy to read about my mistakes)
transform start date and end date to milliseconds new Date(start date).getTime() than compare it (>, <, >=) to the end date also transformed into milliseconds
first select right property
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputStart(e.target.valueAsNumber);
// ...
//...
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputEnd(e.target.valueAsNumber);
then validate
// ..when user submits start validation
const onFormSubmit = (e) => {
e.preventDefault()
// ...
if (start < end) {
// blah blah....
}
}
or better add min and max value so you dont have to check all
<input type="date" name="start" min="2017-01-01" max="2022-01-01">
also please dont use onChage with onClick event listeners its very bad practice
use either one of them, in this case onChage is pretty good.
seems use are using onClick for validating, use useEffect for validating and keep separate state for errors (object).
and check more options here MDN docs and play around with devtools (right click on element, select use in console option and see what properties it holds)
Right now my search function is I have to click the search icon to make it appear the result so I want to change that to real-time search. When I type the name in input it will auto-starting appear the user card for me
Here is my following code:
const [searchQuery, setSearchQuery] = useState("");
const handleChange = (event) => {
event.preventDefault();
setSearchQuery(event.target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
const res = await axios.get(
`/api/v1/search/users/invite/${searchQuery}/${teamId}`
);
setInvitees(res.data[0]);
setShowInvitees(!showInvitees);
};
useEffect(() => {
if (searchQuery === "") {
setInvitees([]);
}
}, [searchQuery]);
<form onSubmit={handleSubmit}>
<div className="invitees-search">
<Button
className="input invitees--search-icon"
style={{ color: "white", backgroundColor: "#00B790" }}
type="submit"
>
<SearchIcon />
</Button>
<input
className="invitees--search_input"
type="text"
name="name"
onChange={handleChange}
placeholder="Name"
aria-label="Search bar"
pattern="^[a-zA-Z0-9 ]+"
required
/>
</div>
</form>
How can I make it auto-populate when my search query length is >= 2 letters?
You can do so with the help of useEffect
Explanation:
I renamed your handleSubmit function to getInvitees
Calling getInvitees function when searchQuery length is more than or equal to 2
Solution
useEffect(() => {
if(searchQuery === "") {
setInvitees([]);
}
if((searchQuery||'').length >= 2) {
getInvitees();
}
}, [searchQuery]);
const getInvitees = async () => { // renamed handleSubmit function to getInvitees
const res = await axios.get(`/api/v1/search/users/invite/${searchQuery}/${teamId}`);
setInvitees(res.data[0]);
setShowInvitees(!showInvitees);
};
I want to get input from a user and compare it with the response I am getting from API, and conditionally render the information if it match or just show a sorry message,(the API only contain 1 set of a data object including 4 value) let me know what am I missing.
here is my code
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatInput, setVatInput] = useState("");
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
const [result, setResult] = useState(false);
const handelVatState = (event) => {
setVatInput(event.target.value);
};
const closeModalHandler = () => {
setResult(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("Some URL")
.then((response) => {
setResponseVatState(response.data.response.data.VATNumber);
setResponseCountryCodeState(response.data.CountryCode);
})
.catch((error) => {
console.log(error);
});
};
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{result ? (
<div>{vatInput}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
and the error is
react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
so I get the input from the user and set it with hooks, then with Axios call get my data, then I split the string with
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
to compare with the input I have, if it's the same then render the data if not just render the sorry message
You have a couple of issues, the main of which resulting in Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. is due to an infinite loop of component re-rendering, which you force by setting state directly in the function body.
More specifically, this code:
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
force react to re-evaluate the component because you're changing its state by using setResult. When react starts rendering the new body it yet again encounters setResult which results in a new update and re-render which, as you see, leads to a never-ending loop.
Furthermore, you don't need to save the request response to the component state at all, as it is relevant just for the calculation, which is needed only in the form submit handler itself. So, you should ditch the
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
state variables altogether. The only state you need except the input value is the validation result.
Also, you have a typo: setResponseVatState(response.data.response.data.VATNumber); should be setResponseVatState(response.data.VATNumber);.
Try this:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(false);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("[URL]")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{isVatValid ? (
<div>{vatValue}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
Also, I suppose <div clicked={closeModalHandler}> should be <div onClick={closeModalHandler}>?
EDIT:
Here is your solution after comments:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(null);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(null);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("https://vat.erply.com/numbers?vatNumber=BG999999999")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
const getResultRepresentation = () => {
if (isVatValid === null) {
return null;
}
if (isVatValid) {
return (
<>
<label className="text-muted">Result: </label>
<div>{vatValue}</div>
</>
);
}
else {
return (
<div onClick={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
);
}
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
value={vatValue} // <= missing
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
{getResultRepresentation()}
</div>
);
}
export default Form;
And here is a CodeSandbox to test it out.
I try to show all data onChange inputs in Object to post it in API in React and I tried the following:
import React, { useState} from "react";
const Counter = () => {
const [form, setForm] = useState({});
const FormData = (event, {name, value}) => {
setForm({...form, [name]: value});
};
return (
<div>
<input value={form.username || ''} name="username" onChange={FormData} type="text"/>
<input value={form.email || ''} name="email" onChange={FormData} type="email"/>
<input value={form.password || ''} name="password" onChange={FormData} type="password"/>
</div>
);
}
export default Counter;
But it shows issue onChange : "Cannot destructure property 'name' of 'undefined' as it is undefined"
It can be done as follows:
const Counter = () => {
const [form, setForm] = useState({});
const FormData = (event) => {
const { target: { value, name } } = event;
setForm({...form, [name]: value});
};
return (
<div>
<input value={form.username || ''} name="username" onChange={FormData} type="text"/>
// same for other inputs
</div>
);
}