Validation input field - javascript

In this mini deposit app I need to validate the input field for 3 different things. With error message that states "Can not be blank", "only numerical values", and "no negative numbers". I have the functionality but tried to implement validation and failed. This is the code without any validation.
function Deposit(){
const ctx = React.useContext(UserContext);
const [totalState, setTotalState] = React.useState(0);
let transactionState = 0; // state of this transaction
let status = `Current Balance $ ${totalState}`;
console.log("Render Account");
const handleChange = event => {
console.log(`handleChange ${event.target.value}`);
transactionState = Number(event.target.value);
};
const handleSubmit = (event) => {
setTotalState(totalState + transactionState);
event.preventDefault();
};
const ATMDeposit = ({ onChange }) => {
return (
<label className="label huge">
Deposit Amount:
<input type="number" onChange={onChange}></input>
<input type="submit" className="btn btn-light" value="Deposit"></input>
</label>
);
};
return (
<Card
bgcolor="primary"
header="Deposit"
body={(
<>
<form onSubmit={handleSubmit}>
<h3>{status}</h3>
<ATMDeposit onChange={handleChange}> Deposit</ATMDeposit>
</form>
</>
)}
/>
)
}
I have implemented the validation code suggested, greatly appreciated, and now validation works! I only need to figure out why the functionality isn't working. The value gets taken from handleChange but it does not add it to setTotalState. I tried adding Number constructor in handleChange like I originally had but it did not work. This is what I have now.
function Deposit(){
const [show, setShow] = React.useState(true);
const [errors, setErrors] = React.useState([]);
const [num, setNum] = React.useState('');
const [totalState, setTotalState] = React.useState(0);
const ctx = React.useContext(UserContext);
let transactionState = 0; // state of this transaction
let status = `Current Balance $ ${totalState}`;
console.log("Render Account");
function validate(value){
const errorsList = [];
if (!value.trim()){
errorsList.push('Error: Cannot be empty');
}
if (/^[-0-9]*$/.test(value) === false){
errorsList.push('Error: only numbers');
}
if (/^[-0-9]*$/.test(value) === true && value < 0) {
errorsList.push('Error: negative values not allowed');
}
if (errorsList.length) {
setErrors(errorsList);
setTimeout(() => setErrors(''),3000)
return false;
}
return true;
}
const handleSubmit = () => {
if (!validate(num)){
setShow(false);
}
if (validate(num)) {
setTotalState(totalState + transactionState);
event.preventDefault;
}
}
const handleChange = event => {
console.log(`handleChange ${event.target.value}`);
transactionState = setNum(event.target.value);
}
return(
<Card
bgcolor="primary"
header= "Make deposit"
status={errors}
body={(
<>
<h3>{status}</h3>
Deposit Amount<br/>
<input type="input" className="form-control" id="num" value={num} onChange={handleChange}></input><br/>
<button type="submit" className="btn btn-light" onClick={handleSubmit}>Deposit</button>
</>
)}
/>
)
}

I would change errors to array
const [errors, setErrors] = React.useState([]);
and here is validation function
function validate(value){
const errorsList = [];
if (!value.trim()){
errorsList.push('Error: Cannot be empty');
}
if (/^[-0-9]*$/.test(value) === false){
errorsList.push('Error: only numbers');
}
if (/^[-0-9]*$/.test(value) === true && value < 0) {
errorsList.push('Error: negative values not allowed');
}
if (errorsList.length) {
setErrors(errorsList);
setTimeout(() => setError(''),3000)
return false;
}
return true;
}
validate("1!") // false
validate("123") // true
Hope this will help

Related

React form how to get user data based on a toggle on/off

I have a form in React JS with one toggle/switch. If toggle/switch is on, then two inputs appear in the screen. So i want to get user data if the user types in inputs and the toggle/switch is on and stays on. So if the user types in inputs but he toggles/switches again to off then input values get reset and when he saves the form i must get empty user data(i get the initial values). How can i achieve something like this? I'm checking in submit handler if the switch button is false and im setting the usestate to the initial values, but it doesnt work.
My code:
Form.js
import React, { useRef, useState } from "react";
import Wrapper from "./UI/Wrapper";
import Switch from '#mui/material/Switch';
import "./Form.css";
const Form = () => {
const [showCertification, setShowCertification] = useState(false);
const [enteredCodecert, setEnteredCodecert] = useState('');
const codecertRef = useRef();
const [codesteps, setCodesteps] = useState([{ value: null }]);
const codestepsRef = useRef();
const enteredCodecertIsValid = showCertification && enteredCodecert.trim() !== '';
const codecertInputIsInvalid = !enteredCodecertIsValid;
const codestepsIsValid = showCertification && codesteps.length >= 1 && codesteps.every(codestep => codestep.value !== null && codestep.value.trim() !== '');
const codestepInputIsInvalid = !codestepsIsValid;
const showCertificationHandler = (event) => {
setShowCertification(prevState => !prevState);
if (!showCertification) {
setEnteredCodecert('');
setCodesteps([{value: null}]);
}
}
const codecertChangeHandler = (event) => {
setEnteredCodecert(event.target.value);
}
const stepChangeHandler = (i, event) => {
const values = [...codesteps];
values[i].value = event.target.value;
setCodesteps(values);
}
const addStepHandler = (event) => {
event.preventDefault();
const values = [...codesteps];
values.push({ value: null });
setCodesteps(values);
}
const removeStepHandler = (i, event) => {
event.preventDefault();
const values = [...codesteps];
values.splice(i, 1);
setCodesteps(values);
}
const submitHandler = (event) => {
event.preventDefault();
if (!enteredCodecertIsValid && showCertification) {
codecertRef.current.focus();
return;
}
if (!codestepsIsValid && showCertification) {
if (codesteps.length >= 1) {
codestepsRef.current.focus();
return;
}
return;
}
if (showCertification === false) {
setEnteredCodecert('');
setCodesteps([{value: null}]);
}
console.log(enteredCodecert);
console.log(codesteps);
}
return (
<Wrapper>
<form onSubmit={submitHandler}>
<fieldset className={`${(showCertification && codecertInputIsInvalid) || (showCertification && codestepInputIsInvalid) ? 'govgr-form-group__error' : '' }`}>
<legend><h3 className="govgr-heading-m">Certifications</h3></legend>
<Switch id="certification" checked={showCertification} onClick={showCertificationHandler} inputProps={{ 'aria-label': 'controlled' }} />
<label className="govgr-label govgr-!-font-weight-bold cert-label" htmlFor="certification">Certification</label>
{showCertification && (
<div>
<div className="govgr-form-group">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="codecert">Code Certification*</label>
{codecertInputIsInvalid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>Code Certification is required.</p>}
<input className={`govgr-input govgr-!-width-three-quarter ${codecertInputIsInvalid ? 'govgr-error-input' : ''}`} id="codecert" name="codecert" type="text" value={enteredCodecert} ref={codecertRef} onChange={codecertChangeHandler} />
</div>
<div className="govgr-form-group">
<label className="govgr-label govgr-!-font-weight-bold" htmlFor="codestep">Code STEPS*</label>
{codestepInputIsInvalid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>Code STEPS are required.</p>}
{codesteps.map((field, idx) => {
return (
<div key={`${field}-${idx}`}>
<div className="flex-row">
<input className={`govgr-input govgr-input--width-10 input-step ${codestepInputIsInvalid ? 'govgr-error-input' : ''}`} id="codestep" type="text" ref={codestepsRef} value={field.value || ""} onChange={e => stepChangeHandler(idx, e)} />
<button className="govgr-btn govgr-btn-warning remove-step" onClick={(e) => removeStepHandler(idx, e)}>Χ</button>
</div>
</div>
);
})}
<button className="govgr-btn govgr-btn-secondary button-step" onClick={addStepHandler}>Add Code Step</button>
</div>
</div>
)}
</fieldset>
<button className="govgr-btn govgr-btn-primary btn-center" type="submit">Save</button>
</form>
</Wrapper>
);
};
export default Form;
The issue is that in showCertificationHandler when you toggle the showCertification you are expecting the state update to be immediate.
const showCertificationHandler = (event) => {
setShowCertification(prevState => !prevState);
if (!showCertification) {
setEnteredCodecert('');
setCodesteps([{value: null}]);
}
}
This is not the case with React state updates, however. React state updates are enqueued and asynchronously processed.
To resolve, move the "reset" logic into an useEffect hook with a dependency on the showCertification state.
const showCertificationHandler = () => {
setShowCertification((prevState) => !prevState);
};
useEffect(() => {
if (!showCertification) {
setEnteredCodecert("");
setCodesteps([{ value: null }]);
}
}, [showCertification]);
For the same reason above, when resetting the states in your submitHandler they are enqueued and asynchronously processed, so console logging the state immediately after will only ever log the state values from the current render cycle, not what they will be on a subsequent render cycle. You can remove the "reset" logic from submitHandler.
const submitHandler = (event) => {
event.preventDefault();
if (!enteredCodecertIsValid && showCertification) {
codecertRef.current.focus();
return;
}
if (!codestepsIsValid && showCertification) {
if (codesteps.length >= 1) {
codestepsRef.current.focus();
return;
}
return;
}
console.log({enteredCodecert, codesteps});
};

Simulate "shift" pressing key on checkbox to select multiple rows

I have the following input
<input type="checkbox" checked={isChecked}
onChange={handleOnChange}/>
and my function is this
const handleOnChange = () => {
let element:any = document.querySelector('input');
element.onkeydown = (e: { key: any; }) => alert(e.key);
element.dispatchEvent(new KeyboardEvent('keydown',{'key':'Shift'}));
setIsChecked(!isChecked);
};
This checkbox is created dinamically as I add new rows and I would like to simulate holding the key "shift" so that when I check multiple checkboxes these rows remain selected.
I am using reactjs.
There's no native way to do it but you can implement it based on the index you have checked.
const checkboxes = new Array(20).fill(null);
export default function App() {
const [checked, setChecked] = useState([]);
const lastChecked = useRef(null);
const handleChange = useCallback((e) => {
const index = Number(e.target.dataset.index);
if (lastChecked.current !== null && e.nativeEvent.shiftKey) {
setChecked((prev) => {
const start = Math.min(lastChecked.current, index);
const end = Math.max(lastChecked.current, index);
return uniq([...prev, ...range(start, end), end]);
});
return;
}
if (e.target.checked) {
lastChecked.current = index;
setChecked((prev) => [...prev, index]);
} else {
lastChecked.current = null;
setChecked((prev) => prev.filter((i) => i !== index));
}
}, []);
return (
<div>
{checkboxes.map((_, i) => (
<div key={i}>
<label>
<input
checked={checked.includes(i)}
data-index={i}
type="checkbox"
onChange={handleChange}
/>
checkbox {i}
</label>
</div>
))}
</div>
);
}

saving input data to different keys

I'm new to Redux work, trying to learn by doing. Here I have AntD input, when user writes something then it saves it to the object keys billingName: and billingContactPerson, but I have also two buttons sender and receiver, when user clicks sender button then it takes data from redux and put it to input, but my question is how to save that data to the same billingName and billingContactPerson. I have tried to save it in useEffect billingName = PickUpName, but it did not save it.
My code:
let billingName: any;
let billingContactPerson: any;
const userData = useSelector(selectUserData);
const dispatch = useDispatch();
const DeliveryName = userData.deliveryName;
const PickUpName = userData.pickUpName;
const DeliveryContactPerson = userData.deliveryContactPerson;
const PickUpContactPerson = userData.pickUpContactPerson;
const [name, setName] = useState(billingName);
const [contactPerson, setContactPerson] = useState(
billingContactPerson
);
const [payer, setPayer] = useState("");
useEffect(() => {
const names = () => {
if (payer === "receiver") {
billingName = DeliveryName;
dispatch(changeUserData({ ...userData, billingName }));
}
if (payer === "sender") {
billingName = PickUpName;
dispatch(changeUserData({ ...userData, billingName }));
} else {
return billingName;
}
};
setName(names);
const contactPersons = () => {
if (payer === "receiver") {
billingContactPerson = DeliveryContactPerson;
dispatch(changeUserData({ ...userData, billingContactPerson }));
}
if (payer === "sender") {
billingContactPerson = PickUpContactPerson;
dispatch(changeUserData({ ...userData, billingContactPerson }));
} else {
return billingContactPerson;
}
};
setContactPerson(contactPersons);
}, [payer]);
const senderPays = (e: any) => {
e.preventDefault();
setPayer("sender");
};
const receiverPays = (e: any) => {
e.preventDefault();
setPayer("receiver");
};
<div>
<Button onClick={senderPays}>sender</Button>
<Button onClick={receiverPays}>receiver</Button>
<Form.Item
label={t("o.billingName")}
name="billingName"
initialValue={userData["billingName"] || name || ""}
>
<Input
onChange={(e: any) =>
dispatch(
changeUserData({ ...userData, billingName: e.target.value })
)
}
type="string"
/>
</Form.Item>
<Form.Item
label={t("orders.ContactPerson")}
name="billingContactPerson"
initialValue={
userData["billingContactPerson"] ||
contactPerson ||
""
}
>
<Input
onChange={(e: any) =>
dispatch(
changeUserData({
...userData,
billingContactPerson: e.target.value,
})
)
}
type="string"
/>
</Form.Item>
</div>
If you're new I recommend you to start with reduxjs/toolkit. It is the new recommended way of writting redux logic.
Let's Learn modern redux
About the question you asked. You can try triggering an function after the redux logic or after sending the data to the input field and give type to billingName and billingContactPerson. So, that you can more catch errors.

real time populate search with react

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);
};

How can I add multiple functions to the React js button?

I want to add startClick to BackButton as in NextButton. In other words, when the BackButton is clicked, the startClick function should work first, then the dispatch (giveForm2PreviousStep (props.currentStep, props.goToStep)) method should work in order. How can I do that?
Question JS
const Question = props => {
const dispatch = useDispatch()
const loading = useSelector(state => state.app.isLoading)
const error = useSelector(state => state.app.error)
const reduxF2 = useSelector(state => state.app.forms.f2)
const [input, setInput] = useState({
value: reduxF2.PastReceivables.value,
valid: true,
})
const changeSelected = val => {
setInput({ ...input, value: val })
}
useEffect(() => {
setInput({ ...input, value: reduxF2.PastReceivables.value })
}, [reduxF2.PastReceivables.value])
useEffect(() => {
if (reduxF2.bulkSaved && props.currentStep === 2) {
dispatch(giveForm2NextStep(props.currentStep, props.goToStep))
dispatch(resetForm2SavedStatus())
}
}, [reduxF2.bulkSaved])
const startClick = e => {
if (input.value === null || input.value === '') {
setInput({ ...input, valid: false })
} else {
setInput({ ...input, valid: true })
const questions = getPastReceivablesArray('PastReceivables', input.value, reduxF2)
if (questions.length == 0) {
dispatch(giveForm2NextStep(props.currentStep, props.goToStep))
} else {
dispatch(updateForm2(questions))
}
}
}
return (
<>
<MyProgressBar now='8' />
<Question>Question here</Question>
<QuestionForm>
<NumericInput
valid={input.valid}
onChange={changeSelected}
value={input.value}
/>
</QuestionForm>
<div className='d-flex justify-content-between'>
<BackButton onClick={() => dispatch(giveForm2PreviousStep(props.currentStep, props.goToStep))} />
<NextButton onClick={startClick} loading={loading} />
</div>
<Warning error={error} />
</>
)
}
BackButton JS
const BackButton = ({ text = 'Back', onClick = null, loading = false, width = '7.5rem' }) => {
return (
<Button
variant='secondary'
className='back-button'
onClick={onClick}
disabled={loading}
style={{width}}
>
<MySpinner loading={loading} />
{!loading && <>{text}</>}
</Button>
)
}
You can call multiple functions in onClick event like below
<BackButton
onClick={(e) => {
startClick(e);
dispatch(giveForm2PreviousStep(props.currentStep, props.goToStep))
}}
/>
You can call multiple function in onclick or else you can send call backs to startclick.
so the call backs will be executed after startclick.
easy to give all the fucntions in onClick itself.

Categories