im trying to create a discord clone, and when i hit enter when the input is empty it shows an empty message, any idea how to prevent that from happening? im new to react, firebase and redux.
const user = useSelector(selectUser);
const channelId = useSelector(selectChannelId);
const channelName = useSelector(selectChannelName);
const [input, setInput] = useState("");
const [messages, setMessages] = useState([]);
useEffect(() => {
if (channelId) {
db.collection('channels')
.doc(channelId)
.collection('messages')
.orderBy('timestamp', 'desc')
.onSnapshot((snapshot) =>
setMessages(snapshot.docs.map((doc) => doc.data()))
);
}
}, [channelId])
const sendMessage = (e) => {
e.preventDefault();
db.collection('channels').doc(channelId).collection('messages').add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
message: input,
user: user,
});
setInput('')
}
return (
<div className="chat">
<ChatHeader channelName={channelName}/>
<div className="chat__messages">
{messages.map((message) => (
<Message
timestamp={message.timestamp}
message={message.message}
user={message.user}
/>
))}
</div>
<div className="chat__input">
<AddCircleIcon fontSize="large"/>
<form>
<input
id="input"
value={input}
disabled={!channelId}
onChange={e => setInput(e.target.value)}
placeholder={`Message #${channelName}`}
/>
<button onClick={sendMessage} className="chat__inputButton" type="submit">
Send Message
</button>
</form>
<div className="chat__inputIcons">
<CardGiftcardIcon fontSize="large"/>
<GifIcon fontSize="large"/>
<EmojiEmotionsIcon fontSize="large"/>
</div>
</div>
</div>
)
}
export default Chat
All you need to do is prevent your submit function from calling firebase if your input is empty.
const sendMessage = (e) => {
e.preventDefault();
if (input.length <= 0) return; // This will end the function here if your input is empty
db.collection('channels').doc(channelId).collection('messages').add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
message: input,
user: user,
});
setInput('')
}
Just check if is some value in it. Empty string is falsable so you can check it in one line.
<input onChange={e => e.target.value && setInput(e.target.value)}/
But in your case probably you send your form on enter. So you can block sending form with empty input by setting validation pattern
<input pattenr=".+" />
or make validation in sumbmit function.
Related
I tried to pass the username and password input by using useRef() for the registration process through the register form. After click button to submit it, it said required username and password. I check the network payload at browser, it only contain email without username and password.
Below are the code
import { useRef, useState } from "react";
import "./register.scss";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const Register = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [username, setUsername] = useState("");
const navigate = useNavigate();
const emailRef = useRef();
const passwordRef = useRef();
const usernameRef = useRef();
// Send email to appear password
const handleStart = () => {
setEmail(emailRef.current.value);
};
// Send username & password for membership
const handleFinish = async (e) => {
e.preventDefault();
setPassword(passwordRef.current.value);
setUsername(usernameRef.current.value);
try {
await axios.post("auth/register", { username, email, password });
navigate("/login");
} catch (err) {
console.log(err);
}
};
return (
<div className="register">
<div className="wrapper">
<div className="header">
<img src="./assets/logo.png" alt="" className="logo" />
<button className="login-btn">Sign In</button>
</div>
</div>
<div className="container">
<h1>Unlimited movies, TV shows and more</h1>
<h2>Watch anywhere. Cancel anytime.</h2>
<p>
Ready to watch? Enter your email to create or restart your membership.
</p>
{!email ? (
<div className="input">
<input type="email" placeholder="Email address" ref={emailRef} />
<button className="register-btn" onClick={handleStart}>
Get Started
</button>
</div>
) : (
<form className="input">
<input type="username" placeholder="Username" ref={usernameRef} />
<input type="password" placeholder="Password" ref={passwordRef} />
<button className="register-btn" onClick={handleFinish}>
Start
</button>
</form>
)}
</div>
</div>
);
};
export default Register;
Here are the screenshot for network payload
Payload
[Preview2
You're trying to access state that hasn't update yet.
If you're using refs, you can remove the useState hooks and change your code to something like below.
const handleFinish = async (e) => {
e.preventDefault();
try {
await axios.post("auth/register", { username: usernameRef.current.value , email: emailRef.current.value, password: passwordRef.current.value });
navigate("/login");
} catch (err) {
console.log(err);
}
Controlled components would be a better option for handling form elements imo.
https://reactjs.org/docs/forms.html#controlled-components
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
Just asking how to get this and put it to my API. Here's a link: https://web.5writer.com/user/signup
{
"countryCallingCode": "374",
"nationalNumber": "23131223",
"number": "+37423131223",
"country": "AM"
}
This is the body of my API
{
dial_code,
mobile,
iso_code
}
This is my code
export default function Home() {
const toast = useToast()
const router = useRouter();
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [dial_code, setDial] = useState('');
const [mobile, setMobile] = useState('');
const [iso_code, setIso] = useState('');
async function handleSubmit (e) {
e.preventDefault();
setLoading(true);
fetch(`https://web.5writer.com/user/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dial_code,
mobile,
iso_code,
}),
})
.then((res) =>
res.json().then((body) => ({
status: res.status,
body,
}))
)
.then((resp) => {
console.log(resp);
setLoading(false);
if (resp.body.status === true) {
setDial('');
setMobile('');
setIso('');
toast({
title: 'Success!',
description: resp.body.message,
status: 'success',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
router.push('../AllOrders/dashboard');
}
else {
toast({
title: 'ERROR!',
description: resp.body.message,
status: 'error',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
}
})
}
return (
<div>
{success && <Notification />}
<main>
<Container >
<Box
w='17.8em'
p={0}
borderRadius='5px'
mt={3}
mb={-4}
mx='auto'
pos='relative'
marginLeft='-1em'
>
{loading && (
<Progress
pos='absolute'
top='0'
left='0'
width='100%'
isIndeterminate
borderTopLeftRadius='6px'
borderTopRighRtadius='6px'
size='sm'
colorScheme='blue'
/>
)}
<form onSubmit={handleSubmit}>
<FormControl className="">
<PhoneNumber
placeholder="enter phone number"
value={dial_code}
onChange={(e) => setDial(e.target.value)}
/>
</FormControl>
<div className="form-group2 d-md-flex">
<div className="w-50 text-left">
<input type="checkbox" className="checkL"/>
<div className="remember">
I have read the <a className="terms">Terms and Condition</a>
</div>
</div>
</div>
<Button
type='submit'
mt='0'
size='sm'
colorScheme='#2CBEFF'
disabled={loading}
pos='relative'
className="lbutton"
>
Register
{/* {loading && <Spinner pos='absolute' color='red.500' />} */}
</Button>
<div className="form-group3">
<p className="text-center">Already have an account?
<Link href="/Login"><a data-toggle="tab" className="Log">Log In</a></Link></p>
</div>
</form>
</Box>
</Container>
</main>
</div>
);
}
This code is working but the problem is I only got one data using onChange. Is it possible to use 3 onChange? or is there any method to get 3 data in just one input.
Give me a piece of advice thank you.
It's still not entirely clear what your issue is, but based on the comments it seems you want a single state variable and change handler to manage 3 inputs. You generally accomplish this by associating a name attribute with each input. The name attribute is accessed via the onChange event and can update the specific nested state.
Example:
const initialState = {
country: "",
countryCallingCode: "",
number: ""
};
function App() {
const [{ country, countryCallingCode, number }, setState] = React.useState(
initialState
);
const changeHandler = (e) => {
const { name, value } = e.target; // <-- destructure from event
setState((state) => ({
...state,
[name]: value // <-- use name as dynamic key
}));
};
const submitHandler = (e) => {
e.preventDefault();
const data = {
dial_code: countryCallingCode,
number,
country
};
setState(initialState);
console.log(data);
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<div>
<label>
Country Code
<input
type="text"
value={countryCallingCode}
name="countryCallingCode"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Number
<input
type="text"
value={number}
name="number"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Country
<input
type="text"
value={country}
name="country"
onChange={changeHandler}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
Update
If you want to use the PhoneInput component you need to map its onChange handler to your own since it passes directly the input value to the handler.
const changeHandler = (e) => {
const { name, value } = e.target;
setState((state) => ({
...state,
[name]: value
}));
};
Here the changeHandler is expecting an onChange event object. You can pass any object you like, and so long as it has the correct shape and properties the handler can handle it.
<PhoneInput
value={number}
name="number"
onChange={(value) =>
changeHandler({
target: {
name: "number",
value
}
})
}
/>
Update 2
Ok, I think I understand what you're after now. You want just a single phone number input and then to parse the country code, country, and phone number from the single state.
Check parsePhoneNumber
There's no need for any custom onChange handlers, just update state with the PhoneInput value and when you are ready, parse the state.
const [state, setState] = React.useState(null);
const submitHandler = (e) => {
e.preventDefault();
const {
countryCallingCode: dial_code,
country: iso_code,
number: mobile
} = parsePhoneNumber(state);
const data = {
dial_code,
mobile,
iso_code
};
setState(null);
// do with data now what you need
};
...
<PhoneInput value={state} onChange={setState} />
hi that's a little dirty but you can create your state somethings like this
let [values,setValues] = useState({phoneNumber : '', dial:'', code:''});
let [inputState, setInputState] = useState('phoneNumber');
const onInputChange = (e) => {
const { target : { value } } = e;
setValues(preventValues => ({...preventValues, inputState : value}))
}
const handleSubmit = (inputStateName) => {
// do your functionality then
setInputState(inputStateName);
}
I am getting state values while clicking submit button but I am unable to do the validation for my login form and how to display the error messages below the input field when I enter my input wrong or empty. please give me a solution to this.Thanks in advance.
const Login = () => {
const [state, setState] = useState({
email: "",
password: ""
});
const handleChange = (e) => {
const {id, value} = e.target
setState(prevState => ({
...prevState,
[id]: value
}))
}
const handleSubmitClick = (e) => {
e.preventDefault();
console.log("Authenticated",state);
}
return(
<>
<div className="container">
<div className="title">
<form onSubmit={handleSubmitClick}>
<div className="form-group">
<input
type="email"
className="email"
placeholder="Email"
value={state.email}
onChange={handleChange}/>
</div>
<div className="form-group">
<input
type="password"
className="password"
placeholder="Password"
value={state.password}
onChange={handleChange}/>
</div>
<button type="submit" className="button">Enter</button>
</form>
</div>
</div>
</>
)
}
export default Login;
If you want to perform client-side validation, you can create hook like this:
const useEmailValidation = (email) => {
const isEmailValid = /#/.test(email); // use any validator you want
return isEmailValid;
};
And then you can use this hook in your form component:
...
const isEmailValid = useEmailValidation(state.email);
const isPasswordValid = usePasswordValidation(state.password);
const isFormValid = isEmailValid && isPasswordValid;
return (
...
<input
className={classNames({ 'invalid': !isEmailValid })}
type="email"
value={state.email}
onChange={handleChange}
/>
{!isEmailValid && 'Some error message'}
<button type="submit" disabled={!isFormValid} className="button">Enter</button>
...
);
...
Your validator hook can return validation message instead of boolean, like:
const useEmailValidation = (email) => {
if (!email || email.length === 0) {
return 'Email cannot be empty';
}
const isEmailValid = /#/.test(email); // use any validator you want
if (!isEmailValid) {
return 'Invalid email provided';
}
return null;
};
Also it is a good practice to show validation message only after field was focused before and after user tried to submit the form.
Formik is a great plugin that will help you perform form validation. The examples are also quite clear.
Or you could do something like this:
const Login = () => {
const [error, setError] = useState(null);
const [state, setState] = useState({
email: '',
password: '',
});
const validateEmail = (email) => {
const re =
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
};
const handleChange = (e) => {
const { id, value } = e.target;
setState((prevState) => ({
...prevState,
[id]: value,
}));
};
const handleSubmitClick = (e) => {
e.preventDefault();
if (!validateEmail(state.email)) {
setError('Invalid Email');
}
if (state.password.length < 8) {
setError('Password must be at least 8 chars long');
}
if (!error) {
// No errors.
}
};
return (
<>
<div className='container'>
<div className='title'>
{error && <div style={{ color: 'red' }}>{error}</div>}
<form onSubmit={handleSubmitClick}>
<div className='form-group'>
<input
type='email'
className='email'
placeholder='Email'
value={state.email}
onChange={handleChange}
/>
</div>
<div className='form-group'>
<input
type='password'
className='password'
placeholder='Password'
value={state.password}
onChange={handleChange}
/>
</div>
<button type='submit' className='button'>
Enter
</button>
</form>
</div>
</div>
</>
);
};
export default Login;
For an empty validation you can check it preventing the submit if the field is empty, like
const handleSubmitClick = (e) => {
e.preventDefault();
if(email.trim() === '' || password.trim() === ''){
//Add a h1 or section with the error message
}else{
console.log("Authenticated",state);
}
}
As long as the email field type is equal to email, which is your case, the browser should give an alert if the string is not an email. ("user#example.com")
Within my handleSubmit method if I hardcode the message and publish the method works as intended. However if I replace "hello stomp" with the input state or submit with any input at all I get Uncaught TypeError: Cannot read property 'publish' of undefined" any insight here will be greatly appreciated
export const Comms = () => {
const [messages, setMessages] = useState();
const [input, setInput] = useState("");
const client = new Client();
client.configure({
brokerURL: "ws://localhost:2019/socket",
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
onConnect: function () {
client.subscribe("/topic/messages", function (msg) {
console.log("WS-MESSAGE: ", msg.body);
});
},
});
const handleSubmit = (event) => {
client.publish({ destination: "/topic/messages", body: "Hello stomp" });
event.preventDefault();
};
useEffect(() => {
client.activate();
}, []);
return (
<div className="comms-cont">
<h1 className="comms-header">Messaging</h1>
<form onSubmit={handleSubmit} className="form-1">
<input
className="forminput"
type="text"
name="message"
onChange={(e) => {
setInput(e.target.value);
}}
/>
</form>
</div>
);
};
You want to use a controlled input component. Add the prop value to the input like this:
<input
className="forminput"
type="text"
name="message"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
/>