How to do validation using useRef() - reactjs

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

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 to get textarea data with useState()

I have read page after page and I am not sure what I am doing wrong. My useState works with my other inputs. I am unable to get it to work with my textarea.
Also, what would be the best way to use my data in another component?
import React, { useState } from "react";
const OrderItems = () => {
const [commentText,setCommentText] = useState("")
const onSubmit = (data) => {
console.log(data.commentText);
}
return (
<>
<form id="myForm" onSubmit={handleSubmit(onSubmit)} >
<div>
<label
htmlFor="CommentsOrAdditionalInformation">Comments or Additional Information</label>
<textarea
name = "commentTextArea"
type="text"
id="CommentsOrAdditionalInformation"
value = {commentText}
onChange={e => setCommentText(e.target.value)}
>
</textarea>
</div>
</form>
<button type = "submit" form ="myForm" className="btn_submit" alt = "submit Checkout">
<a href ="/cart/preview"/>
<img src = ""/>
</button>
</>
)
}
You are initializing state outside the function, Please do it like this:
Also, You are logging the wrong state inside onSubmit.
import React, { useState } from "react";
const OrderItems = () => {
const [commentText,setCommentText] = useState("")
const handleSubmit = (evt) => {
evt.preventDefault();
console.log(commentText);
}
return (
<>
<form id="myForm" onSubmit={handleSubmit} >
<div>
<label
htmlFor="CommentsOrAdditionalInformation">Comments or Additional Information</label>
<textarea
name = "commentTextArea"
type="text"
id="CommentsOrAdditionalInformation"
value = {commentText}
onChange={e => setCommentText(e.target.value)}
>
</textarea>
<input type = "submit" value="Submit" className="btn_submit" alt = "submit Checkout"/>
</div>
</form>
</>
)
}
To use the data in another component: If it is a child component pass it as props. Else use state management tools like context or redux.
Also, for routing, I would recommend using React Router. Refer here.
Some things to keep in mind:
import React, { useState } from "react";
const OrderItems = () => {
// Always initialise state inside the component
const [commentText,setCommentText] = useState("")
const handleOnSubmit = event => {
event.preventDefault();
console.log(commentText);
}
return (
// onSubmit should just be a reference to handleOnSubmit
<form id="myForm" onSubmit={handleOnSubmit} >
<div>
<label
htmlFor="CommentsOrAdditionalInformation">Comments or Additional Information
</label>
// You can self-close the textarea tag
<textarea
name="commentTextArea"
type="text"
id="CommentsOrAdditionalInformation"
value={commentText}
onChange={e => setCommentText(e.target.value)}
/>
</div>
// Put the button inside the form
<button type = "submit" form ="myForm" className="btn_submit" alt="submit Checkout">
<a href ="/cart/preview"/>
<img src = ""/>
</button>
</form>
);
}

custom hook to simplify react controlled form

i'd like to simplify form creation avoiding to write for each fied value={} and onChange={} using a custom hook.
this is my code:
https://codesandbox.io/s/busy-noether-pql8x?file=/src/App.js
the problem is that, each time i press the button, the state is cleaned except for the field i've currently edited
import React, { useState } from "react";
import "./styles.css";
const useFormField = (initialValue) => {
const [values, setValues] = React.useState(initialValue);
const onChange = React.useCallback((e) => {
const name = e.target.name;
const value = e.target.value;
setValues( (prevValues) => ({...prevValues,[name]:value}));
}, []);
return { values, onChange };
};
export default function App() {
//hoot to simplify the code,
const {values,onChange} = useFormField({
Salary: "",
Email: ""
})
const onCreateUser = () => {
console.log(values);
};
return (
<div className="App">
<form>
<label htmlFor="Email">Email: </label>
<input name="Email" onChange={onChange} />
<label htmlFor="Email">Salary: </label>
<input name="Salary" onChange={onChange} />
<button type="button" onClick={onCreateUser}>
Send
</button>
</form>
</div>
);
}

Why does react turn my state into [object object]

I've got this straightforward component:
Here it is on CodeSandbox: https://codesandbox.io/s/fast-architecture-fgvwg?fontsize=14&hidenavigation=1&theme=dark
function Home() {
const [name, setName] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log(name);
};
return (
<>
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={setName}
type="text"
placeholder="name"
/>
<button type="submit">
submit
</button>
</form>
</>
);
}
export default Home;
As soon as I click in the input box, it turns into [object object] and I'd love to know why this is happening.
You are not passing a value to your name variable onChange. onChange returns event which you have to get the value from and set the name that way.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [name, setName] = useState("");
const handleSubmit = e => {
e.preventDefault();
console.log(name);
};
return (
<>
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.currentTarget.value)} type="text" placeholder="name" />
<button type="submit">submit</button>
</form>
</>
);
}
The update here is the onChange attribute. You are grabbing the event e and setting the name to whatever that currentTarget value is.
onChange = { (e) => setName(e.currentTarget.value) }
Your onChange handler receives a change event object. If you want the new value you'll need to get it from the event: event.target.value:
<input
value={name}
onChange={e => setName(e.target.value)}
type="text"
placeholder="name"
/>
When you cast a value to a string, like when calling console.log, the value's toString method is invoked, which for objects returns [object Object] by default.
You had onChange set to setName. Setname is a function used to update the value of name.
You need to write a function to handle the name value being updating when the user types in a name. Set onChange equal to that function:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [name, setName] = useState("");
const handleSubmit = e => {
e.preventDefault();
console.log(name);
};
function handleChange(e) {
e.preventDefault();
setName(e.target.value);
}
return (
<>
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={handleChange}
type="text"
placeholder="name"
/>
<button type="submit">submit</button>
</form>
</>
);
}

React TypeScript: Alternatives to UseRef in functional components

Is there another way of getting/setting the values from the dom that is less expensive than useRef()? Is useRef() to be used lightly as the docs suggest?
import React, { useRef, useEffect } from 'react';
const Join: React.FC = () => {
const fullName = useRef<HTMLInputElement>(null);
const email = useRef<HTMLInputElement>(null);
const password = useRef<HTMLInputElement>(null);
const myForm = useRef<HTMLFormElement>(null);
useEffect(() => {
if (myForm.current) myForm.current.reset();
if (fullName.current) fullName.current.focus();
}, []);
return (
<div>
<form ref={myForm}>
<input type='text' ref={fullName} />
<input type='text' ref={email} />
<input type='text' ref={password} />
</form>
</div>
)
}
When the component loads I want to clear the form and focus the
fullName input
You don't need refs for that
I want to clear the form
Make your inputs controlled
Declare an empty string as initial value
const Component = () =>{
const [state, setState] = useState({
email : '',
password : ''
})
const onChange = ({ target: { value, name } }) =>{
setState(prev => ({
...prev,
[name] : value
}))
}
const { email, password } = state
return(
<>
<input value={email} onChange={onChange} id='email'/>
<input value={password} onChange={onChange} id='password' />
</>
)
}
Automatically focus a given input
Just use autofocus for that
<input autofocus/>

Resources