Trying to disable Send button when there are errors - reactjs

I have this form in a react-redux component.
The problem is that I’m trying to make the Send button disabled when the input.trim() is empty.
It is not behaving as expected.
If I type something, the button remains disabled,
export default function TodoInput() {
const [todoText, setTodoText] = React.useState('');
const [errors, setErrors] = React.useState('');
const dispatch = useDispatch()
function validate(text) {
text.trim()
return (text.length !== 0 ? 'Error' : '')
}
function handleInputChange(e) {
setTodoText((e.target.value).trim())
setErrors(validate(e.target.value));
}
function submitButton() {
if (errors.length !== 0)
return <input type="submit" onSubmit={onSubmit} className="btn btn-primary mx-2" disabled/>
else
return <input type="submit" className="btn btn-primary mx-2"/>
}
function onSubmit() {
dispatch(todoAdded(todoText))
}
return (
<div>
<div className="row m-2">
<form className={style.todoForm}
onSubmit={(e) => {
e.preventDefault(); // the default behavior is to refresh the whole window
onSubmit(todoText);
setTodoText('')
}}>
<input type="text"
className="col form-control"
placeholder="Todo Text..."
name="todoText"
value={todoText}
onChange={handleInputChange}
/>
{submitButton()}
</form>
</div>
</div>
)
}
What could it be?
Thanks in advance
Rafael

Related

updating data in edit-form react

I have a kind of todo, but there are several lines in one object.
I need that when editing one of the fields and pressing the save button, the save will work.
Now, in order to save the changes, need to change all three inputs.
Here is my code in stakeblitz
App.js
function App(props) {
const [tasks, setTasks] = useState(props.tasks);
function editTask(id, newName, newTranslate, newNote) {
const editedTaskList = tasks.map((task) => {
if (id === task.id) {
return { ...task, name: newName, translate: newTranslate, note: newNote };
}
return task;
});
setTasks(editedTaskList);
}
const taskList = tasks
.map((task) => (
<Todo
id={task.id}
name={task.name}
translate={task.translate}
note={task.note}
completed={task.completed}
key={task.id}
editTask={editTask}
/>
));
return (
<div className="todoapp stack-large">
<ul
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading">
{taskList}
</ul>
</div>
);
}
export default App;
I thought that the problem was with the handlers in the todo file, most likely there need to get the previous data from the state, and if the field has not been changed, then use this data as changed in the new state. I tried to do something like this but I couldn't find anything.
Todo.js
export default function Todo(props) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState('');
const [newTranslate, setNewTranslate] = useState('');
const [newNote, setNewNote] = useState('');
function handleChange(e) {
setNewName(e.target.value);
}
function handleChangeTranslate(e) {
setNewTranslate(e.target.value);
}
function handleChangeNote(e) {
setNewNote(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
if (!newName.trim()|| !newTranslate.trim() || !newNote.trim()) {
return;
}
props.editTask(props.id, newName, newTranslate, newNote);
setNewName("");
setNewTranslate("");
setNewNote("");
setEditing(false);
}
const editingTemplate = (
<form className="stack-small" onSubmit={handleSubmit}>
<div className="form-group">
<label className="todo-label" htmlFor={props.id}>
New name for {props.name}
</label>
<input
id={props.id}
className="todo-text"
type="text"
value={newName || props.name}
onChange={handleChange}
/>
<input
id={props.id}
className="todo-text"
type="text"
value={newTranslate || props.translate}
onChange={handleChangeTranslate}
/>
<input
id={props.id}
className="todo-text"
type="text"
value={newNote || props.note}
onChange={handleChangeNote}
/>
</div>
<div className="btn-group">
<button
type="button"
className="btn todo-cancel"
onClick={() => setEditing(false)}
>
Cancel
</button>
<button type="submit" className="btn btn__primary todo-edit">
Save
</button>
</div>
</form>
);
const viewTemplate = (
<div className="stack-small">
<div className="c-cb">
<label className="todo-label" htmlFor={props.id}>
{props.name}
</label>
<label className="todo-label" htmlFor={props.id}>
{props.translate}
</label>
<label className="todo-label" htmlFor={props.id}>
{props.note}
</label>
</div>
<div className="btn-group">
<button
type="button"
className="btn"
onClick={() => setEditing(true)}
>
Edit <span className="visually-hidden">{props.name}</span>
</button>
</div>
</div>
);
return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>;
}
By trial and error, I found how to solve my problem.
It is necessary to transfer data from the array to the new state, which will be the initial data for it
Todo.js file
export default function Todo({name, translate, note, editTask, id}) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState(name);
const [newTranslate, setNewTranslate] = useState(translate);
const [newNote, setNewNote] = useState(note);

empty data instead of data from the array when changing the data in the input react js

I have a simple todo list that consists of multiple inputs.
I made the editing functionality and now everything works as it should, but only once. When I change the input data for the first time, it saves everything to an array with the correct data.
And when I want to do it a second time, then in order to save this data, three inputs must be changed.
I want that even when changing one input, the data is saved in an array (data that has not been changed is overwritten).
Stackblitz code
App.js
function App(props) {
const [tasks, setTasks] = useState(props.tasks);
function editTask(id, newName, newTranslate, newNote) {
const editedTaskList = tasks.map((task) => {
if (id === task.id) {
return { ...task, name: newName , translate: newTranslate , note: newNote };
}
return task;
});
setTasks(editedTaskList);
}
const taskList = tasks
.map((task) => (
<Todo
id={task.id}
name={task.name}
translate={task.translate}
note={task.note}
completed={task.completed}
key={task.id}
editTask={editTask}
tasks={tasks}
/>
));
return (
<div className="todoapp stack-large">
<ul
className="todo-list stack-large stack-exception"
aria-labelledby="list-heading">
{taskList}
</ul>
</div>
);
}
export default App;
I did a check and added the save button onClick which outputs the data to the console. It gives the data correctly the first time, and if the same item in the todo is changed the second time, it gives an empty space instead of the data that has not been changed.
Todo.js
export default function Todo({name, translate, note, editTask, id, tasks}) {
const [isEditing, setEditing] = useState(false);
const [newName, setNewName] = useState(name);
const [newTranslate, setNewTranslate] = useState(translate);
const [newNote, setNewNote] = useState(note);
function handleChange(e) {
setNewName(e.target.value)
}
function handleChangeTranslate(e) {
setNewTranslate(e.target.value);
}
function handleChangeNote(e) {
setNewNote(e.target.value)
}
function handleSubmit(e) {
e.preventDefault();
if (!newName.trim()|| !newTranslate.trim() || !newNote.trim()) {
return;
}
editTask(id, newName,newTranslate,newNote);
setNewName("");
setNewTranslate("");
setNewNote("");
setEditing(false);
}
const editingTemplate = (
<form className="stack-small" onSubmit={handleSubmit}>
<div className="form-group">
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newName || name}
onChange={handleChange}
placeholder="write word"
/>
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newTranslate || translate}
onChange={handleChangeTranslate}
placeholder="write translate"
/>
<input
id={id}
className="todo-text"
type="text"
autoComplete='off'
defaultValue={newNote || note}
onChange={handleChangeNote}
placeholder="write note"
/>
</div>
<div className="btn-group">
<button
type="button"
className="btn todo-cancel"
onClick={() => setEditing(false)}
>
Cancel
</button>
<button type="submit" className="btn btn__primary todo-edit" onClick={()=>console.log(newName, newTranslate, newNote)}>
Save
</button>
</div>
</form>
);
const viewTemplate = (
<div className="stack-small">
<div className="c-cb">
<label className="todo-label" htmlFor={id}>
{name}
</label>
<label className="todo-label" htmlFor={id}>
{translate}
</label>
<label className="todo-label" htmlFor={id}>
{note}
</label>
</div>
<div className="btn-group">
<button
type="button"
className="btn"
onClick={() => setEditing(true)}
>
Edit <span className="visually-hidden">{name}</span>
</button>
</div>
</div>
);
return <li className="todo">{isEditing ? editingTemplate : viewTemplate}</li>;
}
Since you want to keep those preview state which was not edit and still print out those state with the one you edit, you can just remove all the "reset state '' you put, since all your initial state from useState already had a value and is not an empty string "" like this
function handleSubmit(e) {
e.preventDefault();
if (!newName.trim()|| !newTranslate.trim() || !newNote.trim()) {
return;
}
editTask(id, newName,newTranslate,newNote);
setEditing(false);
}

How to activate button in condition

const Login = () => {
const [ID, setID] = useState();
const [PW, setPW] = useState();
function handleIdInput(event) {
setID(event.target.value);
console.log(ID);
}
function handlePWInput(event) {
setPW(event.target.value);
console.log(PW);
}
<form>
<div>
<input type="text" id="ID" className="boxes" onChange={handleIdInput} />
</div>
<div>
<input
type="password"
id="password"
className="boxes"
onChange={handlePWInput}
/>
</div>
<button className="boxes" type="button" onClick={goList}>
LogIn
</button>
</form>;
};
I want to activate the button if id includes # and password has at least 7 characters. I'm trying to find a way not to use class and this method. Thank you!
Please make use of disabled property.
<button className='boxes' type='button' onClick={goList} disabled={()=> checkDisabled(ID, PW)}>
LogIn
</button>
The checkDisabled function must return true or false based on your requirements.
const checkDisabled = (id, password) => {
// condition here
if(id.includes('#') && password.length >= 7)
{
return false;
}
return true;
}
Then finally the component code must look like this. Please note the changes I have made related to how the state variables are connected with the input component.
import { useState } from "react";
const Login = () => {
const [ID, setID] = useState("");
const [PW, setPW] = useState("");
function handleIdInput(event) {
setID(event.target.value);
console.log(ID);
}
function handlePWInput(event) {
setPW(event.target.value);
console.log(PW);
}
const checkDisabled = (id, password) => {
// condition here
if (id.includes("#") && password.length >= 7) {
return false;
}
return true;
};
return (
<form>
<div>
<input
type="text"
id="ID"
className="boxes"
value={ID}
onChange={(e) => handleIdInput(e)}
/>
</div>
<div>
<input
type="password"
id="password"
className="boxes"
value={PW}
onChange={(e) => handlePWInput(e)}
/>
</div>
<button
className="boxes"
type="button"
onClick={goList}
disabled={() => checkDisabled(ID, PW)}
>
LogIn
</button>
</form>
);
};
export default Login;
Reference for useState hook.

React function Component Validation

I am a beginner in react. I was working on the react function component with forms using hooks. Can anyone please tell how can I apply validation on email text when it is invalid or empty, and disable the continue button if the form is not valid.
import React, { useState } from "react";
const ForgotPassowrd = () => {
const [emailId, setemailId] = useState("");
const forgotPasswordClick = (event) => {};
return (
<div>
<div className="NewPassword-form form_wrapper">
<div className="form-body">
<form action="#">
<div>
<div className="form-group">
<label htmlFor="password">Email-Id</label>
<div className="input-group">
<input type="text" className="form-control" value={emailId} onChange={(event)=>
setemailId(event.target.value)}/>
</div>
</div>
<button type="button" onClick={forgotPasswordClick} className="btn btn-lg
btn-block">Continue</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default ForgotPassowrd;
**Try it.This may be helpfull for you! If you can any queries comment below.**
const LoginV2 = ({}) => {
// state
const [loginForm, setLoginForm] = useState({
email: undefined,
password: undefined,
emailValid: false,
passwordValid: false,
});
const [error, setError] = useState({ email: undefined, password: undefined });
// state update
const handleLoginForm = (e) => {
checkValidity(e.target.name, e.target.value);
setLoginForm({ ...loginForm, [e.target.name]: e.target.value });
};
// validation function
const checkValidity = (inputName, inputValue) => {
switch (inputName) {
case "email":
let pattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
loginForm.emailValid = pattern.test(inputValue);
break;
case "password":
loginForm.passwordValid = inputValue.length >= 6;
break;
default:
break;
}
};
// form submit
const onSubmitLoginForm = () => {
console.log(loginForm);
if (!loginForm.emailValid) {
setError(prevError => {
return {
...prevError,
email: "Invalid Email Address"
}
});
}
if (!loginForm.passwordValid) {
setError(prevError => {
return {
...prevError,
password: "Password must be at least six characters long"
}
});
}
return (
<div class="row">
<div class="form">
<div class="col span-1-of-2">
<div class="username">
<p class="login-para text-align-center">LOG IN VIA EMAIL</p>
<form method="post" action="#" class="login-form">
{error.email && (
<div class="alert alert-danger">
<p>
{" "}
<strong> {alertText} </strong> {error.email}
</p>
</div>
)}
{error.password && (
<div class="alert alert-danger">
<p>
{" "}
<strong> {alertText} </strong> {error.password}
</p>
</div>
)}
<div class="info-box">
{icon && <i class="fas fa-user-alt login-icon"></i>}
<input
type="text"
name="email"
placeholder="Your Email"
onChangeText={(e) => handleLoginForm(e)}
inputValue={loginForm.email}
/>
</div>
<div class="info-box">
{icon && <i class="fas fa-user-alt login-icon"></i>}
<input
type="password"
name="password"
placeholder="Your Password"
onChangeText={(e) => handleLoginForm(e)}
inputValue={loginForm.password}
/>
</div>
<div class="buttons">
<input type="checkbox" />
<label class="remember" for="#">
Remember me
</label>
<div class="form-btn-disabled" onClick={onSubmitLoginForm}
>
LOGIN NOW
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default LoginV2;
Try below. I have added inline comments for better understanding. Comment your queries if you have any.
// Regex to check valid email
const validEmail = /^[\w-\.]+#([\w-]+\.)+[\w-]{2,4}$/g
import React, { useState } from "react";
const ForgotPassowrd = () => {
const [emailId, setemailId] = useState("");
//State to disable/enable continue button
const [disableBtn, setDisableBtn] = useState(false);
const forgotPasswordClick = (event) => {};
const handleSubmit = e => {
e.preventDefault();
// Do whatever you want to do after you click submit button
}
const handleChange = e => {
setemailId(event.target.value);
setDisableBtn(validEmail.test(e.target.value));
}
return (
<div>
<div className="NewPassword-form form_wrapper">
<div className="form-body">
{/* Remove action and use onSubmit handler*/}
<form onSubmit={handleSubmit}>
<div>
<div className="form-group">
<label htmlFor="password">Email-Id</label>
<div className="input-group">
{/* Introduced name attribute to help you with handleSubmit handler*/}
<input name="email" type="text" className="form-control" value={emailId} onChange={(event)=>
setemailId(event.target.value)}/>
</div>
</div>
<button onClick={forgotPasswordClick} className="btn btn-lg
btn-block" disabled={disableBtn}>Continue</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default ForgotPassowrd;

What mean "form submitting canceled"?

Get warning in console when trying to submit login form (form submission canceled because the form is not connected). If deleting if (isAuthed) return <Redirect to="/additem" />; my form became are connected. In other issues people say 'replace type of button from submit to button'. It doesn't work. How can I connect form?
export function Login() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isAuthed, setIsAuthed] = useState(false);
console.log(isAuthed);
function onChangeUsername(e) {
setUsername(e.target.value)
}
function onChangePassword(e) {
setPassword(e.target.value)
}
function getToken() {
const token = Date.now().toString();
if (username && password) {
localStorage.setItem('userToken', JSON.stringify(token));
setIsAuthed(true)
}
}
//if (isAuthed) return <Redirect to="/additem" />;
return (
<div className="col-md-12">
<div className="card card-container">
<form onSubmit={getToken} noValidate>
<span className="fas fa-sign-in-alt"/>
<div className="form-group">
<label htmlFor="username">Username</label>
<input type="text" className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
placeholder="Login"
required
/>
<div className="invalid-feedback">
Wrong username
</div>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
placeholder="Password"
required
/>
<div className="invalid-feedback">
Wrong password
</div>
</div>
<div className="form-group">
<button className="btn btn-primary" type="submit">
Submit form
</button>
</div>
</form>
</div>
</div>
)
}
First of your form is submitting except you're doing one thing which is not getting you your expected results. You will need to prevent the default behavior with form submission. Preventing the default behavior will prevent the page from reloading which is the normal behavior with form submission.
function getToken(event) {
event.preventDefault();
const token = Date.now().toString();
if (username && password) {
localStorage.setItem('userToken', JSON.stringify(token));
setIsAuthed(true)
//you can then redirect here. Example
//pass in a props then props.history.push("/additem");
}
}

Resources