React how to disable submit button until form values are input - reactjs

I'd like to keep the submit button in my form disabled until the values of the each input are at least one character, not including white space. I tried using trim() and it seems to work until I click submit.
Here is my Form component:
export function Form(props) {
const { form, inputChange, postQuiz } = props;
const onChange = () => {
inputChange()
}
const onSubmit = evt => {
evt.preventDefault()
const question_text_input = document.getElementById("newQuestion");
const question_text = question_text_input.value
const true_answer_text_input = document.getElementById("newTrueAnswer");
const true_answer_text = true_answer_text_input.value
const false_answer_text_input = document.getElementById("newFalseAnswer");
const false_answer_text = false_answer_text_input.value
postQuiz({ question_text, true_answer_text, false_answer_text })
}
return (
<form id="form" onSubmit={onSubmit}>
<h2>Create New Quiz</h2>
<input onChange={onChange} placeholder="Enter question" />
<input onChange={onChange} placeholder="Enter true answer" />
<input onChange={onChange} placeholder="Enter false answer" />
<button
id="submitNewQuizBtn"
disabled={
form.newFalseAnswer.trim().length >= 1
&& form.newTrueAnswer.trim().length >= 1
&& form.newQuestion.trim().length >= 1
? ""
: "disabled"
}
>
Submit new quiz
</button>
</form>
)
}
export default connect(st => st, actionCreators)(Form)
With the code above, the submit button stays disabled until I type at least one character in each input (doesn't count whitespace, like I wanted), but as soon as I click submit I get the error: Uncaught TypeError: Cannot read properties of undefined (reading 'trim').
I don't understand why that happens. Is using trim() on the form Object incorrect?

You can achieve that using two states in your component. One for input and another for the button.
const App = () => {
const [input, setInput] = useState('') // For input
const [isdisabled, setIsDisabled] = useState(false) // For button
// when input is changing this function will get called
const onChange = (e) => {
setInput((prevState) => (e.target.value))
if(e.target.value.trim().length < 1) { // Checking the length of the input
setIsDisabled(true) // Disabling the button if length is < 1
} else {
setIsDisabled(false)
}
}
const onSubmit = (e) => {
e.preventDefault()
// Code...
}
return (
<div className="App">
<form onSubmit={onSubmit}>
<input type='text' placeholder='email' value={input} onChange={onChange} />
<button id='button' type='submit' disabled={isdisabled}>Submit</button>
</form>
</div>
);
}
If you have multiple inputs change the onChange function and input state accordingly.

Related

Onchange in input field is not working while editing a form

I am developing a small application in react, in which I have an edit option. On clicking the edit button, it will load the existing data and allows the user to edit any of the fields and submit.
Fetching the data and loading it in a form are working fine, but when I edit a textbox, the value changes to the existing fetched value, and it is not allowing me to hold the edited value.
Please note, the problem is with editing the input in a form not in submitting. Below is the edit component that I am using.
mport { useState, useEffect } from 'react';
import { json, Link } from 'react-router-dom';
import { useParams } from 'react-router-dom';
const EditTask = ({ onEdit }) => {
const [text, setText] = useState('');
const [day, setDay] = useState('');
const [reminder, setReminder] = useState(false);
const params = useParams();
useEffect(() => {
fetchTask();
});
const fetchTask = async () => {
const res = await fetch(`http://localhost:5000/tasks/${params.id}`);
const data = await res.json();
setText(data.text);
setDay(data.day);
setReminder(data.reminder);
};
const onSubmit = async (e) => {
e.preventdefault();
if (!text) {
alert('Please enter task name');
return;
}
onEdit({ text, day, reminder });
setText('');
setDay('');
setReminder(false);
};
const handleChange = ({ target }) => {
console.log(target.value); // displaying the input value
setText(target.value); // changes to existing value not the one I entered
};
return (
<form className="add-form" onSubmit={onSubmit}>
<div className="form-control">
<label>Task</label>
<input
id="AddTask"
type="text"
placeholder="Add Task"
value={text}
onChange={handleChange}
/>
</div>
<div className="form-control">
<label>Date & Time</label>
<input
id="Date"
type="text"
placeholder="Date & Time"
value={day}
onChange={(e) => setDay(e.target.value)}
/>
</div>
<div className="form-control form-control-check">
<label>Set Reminder</label>
<input
id="Reminder"
type="checkbox"
checked={reminder}
value={reminder}
onChange={(e) => setReminder(e.currentTarget.checked)}
/>
</div>
<input className="btn btn-block" type="submit" value="Save Task" />
<Link to="/">Home</Link>
</form>
);
};
export default EditTask;
Can someone explain what I am missing here? Happy to share other information if needed.
Expecting the input fields to get the value entered and submitting.
You missed adding dependency to useEffect
Yours
useEffect(() => {
fetchTask()
}
)
Should be changed
useEffect(()=>{
fetchTask()
}, [])
becasue of this, fetchTask is occured when view is re-rendered.

How do I add a task in a react to-do app by pressing ENTER key?

I am new to react.js .
I made a simple to-do app to learn CRUD using react.js:
A task is added when I click the '+' button. But I need to add a task when I click the 'ENTER' Key.
What should I do?
Here's A Part Of My Code :
JSX :
function Body() {
const [toDos,setToDos] = useState([])
const [toDo,setToDo] = useState('')
const deleteTodo = idToDelete => setToDos(currentTodos => currentTodos.filter(toDo => toDo.id !== idToDelete))
return (
<div className="bodyoftodo">
<div className="input">
<form onSubmit={toDo} >
<input value={toDo} onChange={(e)=>setToDo(e.target.value)} type="text" placeholder="🖊️ Add item..." />
<i onClick={()=>setToDos([...toDos,{id:Date.now() ,text: toDo, status: false}])} className="fas fa-plus"></i>
</form>
</div>
<div className="todos">
{toDos.map((obj)=>{
return(
<div className="todo">
<div className="left">
<input onChange={(e)=>{
console.log(e.target.checked);
console.log(obj);
setToDos(toDos.filter(obj2=>{
if(obj2.id===obj.id){
obj2.status=e.target.checked
}
You can do it with set a function on onKeyPress event.
handleKeyPress = (event) => {
if(event.key === 'Enter'){
setToDos([...toDos,{id:Date.now() ,text: toDo, status: false}])
setToDo("");
}
}
return(
<div>
<input value={toDo} onChange={(e)=>setToDo(e.target.value)} type="text"
placeholder="🖊️ Add item..." onKeyPress={handleKeyPress} />
</div>
);
}
You could wrap the input and button in a form and include the function to add a task in the onsubmit attribute of the form. That way, the function gets called whether you click the button or press enter.
Like so:
const AddToDo = () => {
// ...state
const [todoText, setTodoText] = useState('');
const handleSubmit = (e) => {
e.preventDefault(); //stop default action of form
//....call add todo function
setTodoText(''); //clear input
}
return (
<form onSubmit={handleSubmit}>
<input type='text' onChange={ ({ target }) => setToDoText(target.value)}>
<button type='submit'>Add ToDo</button>
</form>
)
}
In this case, clicking on the button or pressing enter would trigger the handleSubmit function.
If you are using a form you can do the below by adding on the onSubmit
import { useState } from "react";
export default function App() {
const [todos, setTodos] = useState([]);
const [todo, setTodo] = useState("");
const handleAddTodo = (e) => {
e.preventDefault()
setTodos([...todos, todo]);
setTodo("");
};
const handleChange = (e) => {
setTodo(e.target.value);
};
return (
<div className="App">
<h2>Todos</h2>
<form onSubmit={handleAddTodo}>
<label>
New Todo
<input value={todo} onChange={handleChange} />
</label>
</form>
<ul>
{todos.map((t, i) => (
<li key={i}>{t}</li>
))}
</ul>
</div>
);
}
Else, you can just use the input and hook up a listener on Key Up / Key Down and if the Key Code is Enter (13).You can trigger to add the Todo
const handleKeyUp = (e) =>{
if(e.keyCode === 13){
handleAddTodo()
}
}
Full Example In Codesandbox https://codesandbox.io/s/ancient-sunset-mr55h?file=/src/App.js:239-325
One option could be to make it form by wrapping your input field and button inside the form tag and then give type="submit" to the button
or you could attach event listner with enter key

How to do validation using useRef()

How do I validate input box value using useRef .
Initial validation is not required once user clicks on input box and comes out then it should validate if input box is empty it should show input box cannot be empty.
Codesandbox Link
code i tried. using onBlur
export default function App() {
const name = React.useRef("");
const nameBlurData = (name) => {
console.log("name", name);
};
return (
<div className="App">
<form>
<input
onBlur={() => nameBlurData(name.current.value)}
type="text"
ref={name}
placeholder="Enter First Name"
/>
// show error message here
</form>
</div>
);
}
You can use "useRef" to validate the value of an input field.
No need to use "useState".
Below code is a basic implementation of OP's question
You can replace the "console.log" with your alert component.
import { useRef } from "react";
const ComponentA = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const onBlurHandler = (refInput) => {
if (refInput.current?.value === "") {
console.log(`${refInput.current.name} is empty!`);
}
}
return (
<form>
<input ref={emailRef} onBlur={onBlurHandler.bind(this, emailRef)} />
<input ref={passwordRef} onBlur={onBlurHandler.bind(this, passwordRef)} />
<form/>
)
}
Link to "useRef"
Note: Not tested, code typed directly to SO's RTE
You can use a local state and conditionally render an error message like this:
const [isValid, setIsValid] = useState(true)
const nameBlurData = (name) => {
setIsValid(!!name);
};
return (
<div className="App">
<form>
<input
onBlur={() => nameBlurData(name.current.value)}
type="text"
ref={name}
placeholder="Enter First Name"
/>
{!isValid && <span> input must not be empty </span> }
</form>
Note that you don't really need a ref in this case, you can just use the event object like:
onBlur={(event) => nameBlurData(event.target.value)}
You need to use useState hook to update the value of the name property. Using ref is not ideal here.
Live demo https://stackblitz.com/edit/react-apqj86?devtoolsheight=33&file=src/App.js
import React, { useState } from 'react';
export default function App() {
const [name, setName] = useState('');
const [hasError, setError] = useState(false);
const nameBlurData = () => {
if (name.trim() === '') {
setError(true);
return;
}
setError(false);
};
return (
<div className="App">
<form>
<input
onBlur={nameBlurData}
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="Enter First Name"
/>
{hasError ? <p style={{ color: 'red' }}>Name is required</p> : null}
</form>
</div>
);
}

How to display a ref variable without using state in React?

was wondering if there is any way to directly display the value of a variable from ref without using state, all the examples deal with "alerting" and alert works just fine, I'm trying to figure out to way to display it immediately as well. So, I am simply trying to display the value from the "name" here. Apologies for the x variable naming.
I assume it's not friendly to the DOM.
Thank you.
const UncontrolledExample = () => {
const name = useRef();
let x = '';
const showValue = (e) => {
e.preventDefault();
alert(name.current.value);
x = name.current.value;
return x;
};
return (
<div>
<label>
<input type="text" ref={name}/>
</label>
<button onClick={showValue}>
Display value : {x}
</button>
</div>
)
}
In react, if you want the page to update, you must set state. Your tutorial seems to be showing you how to do uncontrolled components. If you want to keep the input as an uncontrolled component you can, but you still need a state for X. That would look like this:
const UncontrolledExample = () => {
const name = useRef();
const [x, setX] = useState('');
const showValue = (e) => {
e.preventDefault();
setX(name.current.value);
};
return (
<div>
<label>
<input type="text" ref={name}/>
</label>
<button onClick={showValue}>
Display value : {x}
</button>
</div>
)
}
Alternatively, you can turn the input into a controlled component. If you want the display value to only change when the button is pressed, you'll need two states:
const ControlledExample = () => {
const [inputValue, setInputValue] = useState('');
const [x, setX] = useState('');
const showValue = (e) => {
e.preventDefault();
setX(inputValue);
};
return (
<div>
<label>
<input type="text"
value={inputValue}
onChange={(e) => setInputValue(e.currentTarget.value)}
/>
</label>
<button onClick={showValue}>
Display value : {x}
</button>
</div>
)
}
If they should always change simultaneously (ie, without the button), you just need one state:
const ControlledExample = () => {
const [inputValue, setInputValue] = useState('');
return (
<div>
<label>
<input type="text"
value={inputValue}
onChange={(e) => setInputValue(e.currentTarget.value)}
/>
</label>
<p>Display value : {inputValue}</p>
</div>
)
}

DOM does not update on value change

I am trying to set up a simple form using a function component using a next.js project:
const Form = () => {
let error = false
const handleNameSubmit = (e) => {
e.preventDefault()
const name = e.target.name.value.trim()
if(!! name.length) {
error = false
} else {
error = 'Please enter your name'
}
}
return (
<>
<form onSubmit={handleNameSubmit}>
<h1>I’d like to know how to address you,
please type in your name</h1>
<input type="text" name="name" placeholder="Your name"/>
{!!error && (<p>{error}</p>)}
<button type="submit">Next</button>
</form>
</>
)
}
I am doing some trivial validation i.e. checking if any value is entered in the name input and displaying an error, if the field is empty.
However, when I set the value of error on page load to be something, it shows in the DOM, but if I manipulate it later, the DOM does not update. I am new to next.js.
You have to use state variable to achieved your goal. Simply convert error in state variable and it will work.
const Form = () => {
const [error, setError] = useState(false);
const handleNameSubmit = (e) => {
e.preventDefault();
const name = e.target.name.value.trim();
setError(name.length ? false : 'Please enter your name');
}
return (
<>
<form onSubmit={handleNameSubmit}>
<h1>I’d like to know how to address you,
please type in your name</h1>
<input type="text" name="name" placeholder="Your name" />
<button type="submit">Next</button>
{error && <p>{error}</p>}
</form>
</>
)
}
I have created small demo for you.
https://stackblitz.com/edit/react-7kat5c
Hope this will help you!

Resources