Dont set value in Form.Control - reactjs

I have a new form for save data.
I use this code
const [validated, setValidated] = useState(false);
const [newInfo, setNewInfo] = useState({
name: ""
});
const handleChange = (event) => {
const { name, value }=event.target.value;
setNewInfo({
...newInfo,
[name]: value
});
console.log(newInfo.name)
};
and in Form.Control
<Form noValidate validated={validated}>
<Form.Group >
<Form.Label>Name</Form.Label>
<Form.Control required type="text" placeholder="Enter Name" value={newInfo.name}
onChange={handleChange} />
<Form.Control.Feedback type="invalid">Please choose a name.</Form.Control.Feedback>
</Form.Group>
<Button onClick={(e) => handleSubmit(e)} variant="primary">Submit</Button>
</Form>
When i type in input, run onchangehandle and event.target.value has value but dont set in input (input is empty) !!
when i change const [newInfo, setNewInfo] = useState(""); and set a string for it, it is ok .

setNewInfo() which is a setter is asynchronous, which means that at the time you console.log the state, it's not updated yet.
You can use useEffect to track for changes of state.
useEffect(() => {
console.log(newInfo);
},[newInfo.name);

Related

Why do I need a separate handler from checked input?

Unable to get the value of the checked input (true or false).
function Login() {
const [inputs, setInputs] = useState({});
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
setInputs(values => ({
...values,
[name]: value
}));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log({inputs});
};
return (
<form onSubmit={handleSubmit}>
<input type="email" name="mail" value={inputs.mail || ""} onChange={handleChange}/>
<input type="password" name="pass" value={inputs.pass || ""} onChange={handleChange}/>
<div>
<input type="checkbox" name="check" value={inputs.check || false} onChange={handleChange}/>
<label>Remember me</label>
</div>
<button type="submit">Submit</button>
</form>
);
}
export default LoginGoogle
Tried
const handleChange = (event) => {
const name = event.target.name;
const value = event.target.value;
const check = event.target.checked;
setInputs(values => ({
...values,
[name]: value || check
}));
};
For
<input type="checkbox" name="check" value={inputs.checked} onChange={handleChange}/>
And
<input type="checkbox" name="check" checked={inputs.checked} onChange={handleChange}/>
It works, but I am certain I am going about it wrong. Tutorials seem to concentrate on input objects of similar key-values, e.g. multiple checkboxes, multiple text input, and so on.
The reason is that checkbox values never change, only their checked property does.
Ideally, you'd use a different change handler
const onCheckboxChange = ({ target: { name, checked } }) => {
setInputs((prev) => ({
...prev,
[name]: checked,
}));
};
return (
<input
type="checkbox"
name="check"
checked={inputs.check}
onChange={onCheckboxChange}
/>
);

Input value is not being cleared after submit

I am building a to-do list in react and when adding a new task to the list, the input value is not being cleared from the form after submit, I'm trying to achieve this using setInput('');
const Form = (props) => {
const [input, setInput] = useState('');
const handleChange = e => {
setInput(e.target.value);
}
const handleSubmit = e => {
e.preventDefault();
const newTask = {
id: uuidv4(),
text: input,
completed: false
};
props.onSubmit(newTask);
setInput('');
}
return (
<form
className='task-form'
>
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
/>
<button className='task-btn' onClick={handleSubmit}>Add Task</button>
</form>
)
}
export default Form
You aren't using the input state anywhere except when creating the newTask object. You need to pass it as a prop to the <input> component to make it fully controlled and for calls to the state setter to result in changes to the DOM.
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
/>
should be
<input
className='task-input'
type='text'
placeholder='Enter a task'
name='text'
onChange={handleChange}
value={input}
/>

Minlength doesn't work when I add onChange property

I want to prevent using special symbols except - in input and check the min-length and max-length. But When I add onChange property on input tag, the minLength property doesn't work!(maxLength works fine.)
Does anyone how to solve this?
const handleNickname = (e: React.ChangeEvent<HTMLInputElement>) => {
let allowedNick = e.target.value;
allowedNick = allowedNick.replace(/ /gi, "");
allowedNick = allowedNick.replace(
/[~\-+=!?##$%₩^&*|(){}\[\]\\\/'";:<>,]/gi,
""
);
e.target.value = allowedNick;
setNick(allowedNick);
};
<form>
<input type="text" placeholder="text" required={true} minLength={2} maxLength={10} onChange={handleNickname} />
<button type="submit">Submit</button>
</form>
You are manually changing the value, and for some reason this is messing with the form submission validator.
This line breaks - e.target.value = allowedNick;
Making component controlled solved this for me.
export function Test() {
const [text, setText] = useState('') // added state
const handleNickname = (e) => {
let allowedNick = e.target.value;
allowedNick = allowedNick.replace(/ /gi, "");
allowedNick = allowedNick.replace(
/[~\-+=!?##$%₩^&*|(){}\[\]\\\/'";:<>,]/gi,
""
);
setText(allowedNick) // changing state
};
return <form>
<input type="text" placeholder="text" onChange={handleNickname} required={true} minLength={2} maxLength={10} value={text} />
<button type="submit">Submit</button>
</form>
}

useEffect inside customHook not happens sometimes (from unknown reason). Using useRef

I have this custom hook which supposed to make debounce email validation.
Suddenly I notice that it's not happening in all types.
Sometimes in the first types it's happen sometimes not.
See the log of "useEffect" which won't happen (for me - in each case) with any type. And when it's happening it's taking the previous value.
the Custom hook:
export function useDebounceEmailValidation(value, delay) {
console.log("useDebounceEmailValidation ? ")
// State and setters for debounced value
const [valid, setValid] = useState(true);
const initRun = useRef(true);
console.log("init run = " , initRun.current)
useEffect(
() => {
console.log("useEffect?"); //---------------------------> this not happening on each render
//we don't want to do it on initial running
if(initRun.current){
initRun.current = false;
}
else{
// Update debounced value after delay
const handler = setTimeout(() => {
console.log("validating mail - " ,value);
setValid(validateEmail(value));
// setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
}
},
[value, delay] // Only re-call effect if value or delay changes
);
return valid;
}
the form component:
import React, {useLayoutEffect, useRef, useState} from 'react';
import Button from 'react-bootstrap/Button';
import {useDebounceEmailValidation} from "./utils-hooks";
import {Alert, Col, Form} from "react-bootstrap";
export function SubscribersForm() {
const [details, setDetails] = useState({
firstName: "",
lastName: "",
email: "",
tel: ""
});
const [popupMsg, setPopupMsg] = useState("default");
const [showMsg, setShowMsg] = useState(false);
const [isError, setIsError] = useState(false);
const emailChange = useRef(false);
const [validated, setValidated] = useState(false);
//For cases we need to access the email through local state and not by state
// (the value will persist between component re-rendering and the reference updating won't trigger a component re-rendering)
const emailRef = useRef("");
const validEmail = useDebounceEmailValidation(emailRef.current, 600);
// // general layout effect - will be happen on each component update - for DEBUG
// useLayoutEffect(() => {
// console.log("details = ", details);
// });
//happening after change in the popup message
useLayoutEffect(() => {
setTimeout(() => {
//resetting the msg
setPopupMsg("");
setShowMsg(
false);
}, 2000);
}, [popupMsg])
//happen after change in the details
useLayoutEffect(() => {
//handling email changing (validation)
if (emailChange.current) {
emailRef.current = details.email;
console.log("email.current = " , emailRef.current)
}
}, [details]);
const handleChange = (ev) => {
ev.persist();
if (ev.target.name === "email" ) {
emailChange.current = true;
} else {
emailChange.current = false;
}
setDetails(prevDetails => ({
...prevDetails,
[ev.target.name]: ev.target.value
}));
}
const onSubmit = (ev) => {
const form = ev.currentTarget;
//The default validation for the form
if (form.checkValidity() === false || !validEmail) {
ev.preventDefault();
ev.stopPropagation();
setValidated(true);
return;
}
ev.preventDefault();
alert("Those are the details - you can send it from here to the server !!! :) \n\n" +
"name = " + details.firstName + " " + details.lastName
+ "\nemail = " + details.email
+ "\nphone = " + details.tel);
//we set validation to false, because by default you don't want to show validation
setValidated(false);
setPopupMsg("Your details have been successfully saved");
setIsError(false);
setDetails({
firstName: "",
lastName: "",
email: "",
tel: ""
});
setShowMsg(true);
}
return (
<div className="subscribers-input">
<h3>Subscribers - Form</h3>
<Form className="needs-validation" noValidate validated={validated}
onSubmit={onSubmit}>{/*start of the form block */}
<Form.Row className="">{/*start of the form row of 12/12 columns*/}
<Col xs={12} className="">
<Form.Group controlId="firstName" className="">
<Form.Control
type="text"
placeholder="First name"
value={details.firstName}
onChange={handleChange}
name="firstName"
required
size="sm"
aria-label="first name"
/>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="lastName" className="">
<Form.Control
type="text"
placeholder="Last name"
value={details.lastName}
onChange={handleChange}
name="lastName"
required
size="sm"
aria-label="last name"
/>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="email" className="">
<Form.Control
type="email"
placeholder="Email"
value={details.email}
onChange={handleChange}
name="email"
required
size="sm"
aria-label="email"
isInvalid={!validEmail}
/>
<Form.Control.Feedback type="invalid">Email is Invalid</Form.Control.Feedback>
</Form.Group>
</Col>
</Form.Row>
<Form.Row className="">
<Col xs={12} className="">
<Form.Group controlId="tel" className="">
<Form.Control
type="tel"
placeholder="Phone"
value={details.tel}
onChange={handleChange}
name="tel"
required
size="sm"
aria-label="phone"
/>
</Form.Group>
</Col>
</Form.Row>
{showMsg &&
<Alert variant={isError ? 'danger' : 'success'}>{popupMsg}</Alert>
}
<Button type="submit" size="sm">Save</Button>
</Form>
</div>
)
}
See how the log not always happening.
there are two things to know: first, custom hooks only rerun when the component that we use our custom hook in (in our case, "SubscribersForm"), rerenders. second, the useEffect dependency array checks the equality of objects by references.
so to be sure that useEffect can intercept changes in its dependency array object we should pass new references for that object.
in the main component "SubscribersForm" you pass a reference to your "useDebounceEmailValidation" custom hook, so when you change the value of emailRef.current, no rerenders happen, and also the reference of the object is the same as before. you should use states in these cases

React input onChange did'nt fire on same value

Code:
function SingleInput(props: {value: string; onChanged: (value: string) => void}) {
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
console.log("onChange fired with value", newValue) /*FIXME Убрать!*/;
props.onChanged(newValue);
},
[props.value, props.onChanged]
);
return <input type="text" maxLength={1} value={props.value} onChange={handleChange} />;
}
Problem:
Еhe onChange function is not called if I try to select an existing value and enter the same.
Example:
I type 1 on this component. Okay, onChange fired. Then I select this value by mouse and type 1 again - onChange didn't fired.
Question: What should I do so that the function is fired even on the same input?
Thanks!
You need to try with onInput event handler. It will capture event even if you are adding same value.
return <input type="text" maxLength={1} value={props.value} onChange={handleChange} onInput={handleChange} />;
You need to setup another state to store the all the uploaded files and clear the "onChange" - props.onChanged("");
i.e. like below so you can upload the same file and the actual file is located at "file"
const [file, setFile] = useState("")
const [inputValue, setInputValue] = useState("")
<input
type="file"
value={inputValue}
onChange={(e) => {
setFile(e.target.files[0])
setInputValue("")
}}
/>

Resources