I have an input with an onChange event handler that is refreshing the screen every time I try to type some character inside. I would like to know if any of you guys could give me a hand to solve it.
Here is my code:
const HeaderUser: React.FC<TabWrapper> = () => {
const [drawerEmpreendimentosVisible, setDrawerEmpreendimentosVisible] = useState(false);
const intl = useIntl().formatMessage;
const [currentEmpreendimento] = useLocalStorage().createState('currentEmpreendimento');
const [displayRoadmapScreen, setDisplayRoadmapScreen] = useState(false)
const [displayRegisterScreen, setDisplayRegisterScreen] = useState(false);
const [inspector, setInspector] = useState('');
const [roadmap, setRoadmap] = useState('');
const RegisterScreen = () => {
const handleOk = () => {
setDisplayRegisterScreen(false);
};
const handleCancel = () => {
setDisplayRegisterScreen(false);
};
return (
<div>
<Modal
title="Cadastrar novo roteiro"
visible={displayRegisterScreen}
onOk={handleOk}
onCancel={handleCancel}>
<b>Roteiro:</b>
<br />
//Refreshing issue <Input type="text" placeholder="Roteiro" onChange={e =>
setRoadmap(e.target.value)} /><br />
<b>Inspetor:</b>
<br />
<Input type="text" name="inspector" placeholder="Inspetor" onChange={(e) => console.log(e.target.value)} />
</Modal>
</div>
)
};
Could it be because you have defined your state outside the RegisterScreen component?
Try defining it within the component.
const RegisterScreen = () => {
const [roadmap, setRoadmap] = useState('');
//rest of code...
Related
I have such a functional component. When a user enters input, I send a message to the server and output to other users what someone writes. Here is the code:
const ChatInput = (props) => {
const [message, setMessage] = useState('');
const typingMessage = () =>{
socket.emit('typing',props.username);
}
return (
<div className>
<Input
value = {message}
onChange = {
(e) => typingMessage(e.target.value)
}
placeholder="Type a message here"
/>
<Button
onClick={sendMessage}
icon={<SendOutlined />
}/>
</div>
);
};
How do I track that the user has stopped writing? If he does not enter anything into the input for more than 10 seconds?
You need deboune function, that will count 10sec (it may be different time, depends on you) after last input (onChange trigger)
function debounce(func, timeout = 10000){
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
}
function saveInput(){
console.log('User has stopped writting 10 sec ago');
}
const processChange = debounce(() => saveInput());
const typingMessage = () =>{
socket.emit('typing',props.username);
processChange()
}
You can use a combination of onFocus, onBlur, and onMouseLeave, in such a way that when onFocus happens, he is writing, when onBlur happens, he is not writing anymore, and when onMouseLeave happens or when he clicks on send, you trigger onBlur yourself. Like below:
I assumed that your Input component can forward ref. If not make it so by following Forwarding Refs.
const ChatInput = (props) => {
const [message, setMessage] = useState("");
const inputRef = useRef();
const typingMessage = () => {
socket.emit("typing", props.username);
};
const notTypingMessage = () => {
socket.emit("typing", "");
};
return (
<div className>
<Input
ref={inputRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
onFocus={() => typingMessage()}
onBlur={() => notTypingMessage()}
onMouseLeave={() => inputRef?.current.blur()}
placeholder="Type a message here"
/>
<Button onClick={()=>{sendMessage(); inputRef?.current.blur()}} icon={<SendOutlined />} />
</div>
);
};
export default ChatInput;
This is a one solution waiting for a second after finish typing and emits stoppedTyping event.
you may still want to optimize it according to the your applications needs
const emit = (action, data) => {
console.log(action, data)
}
function App() {
const props = { username: 'me' }
const [message, setMessage] = useState('');
const [isTyping, setIsTyping] = useState(false);
useEffect(() => {
if (message && !isTyping) {
emit('stoppedTyping', props.username)
}
}, [isTyping])
const typingMessage = (v) => {
emit('typing', props.username);
setMessage(v)
}
const sendMessage = (e) => {
console.log(e)
}
return (
<div className=''>
<input
value={message}
onKeyDown={() => !isTyping && setIsTyping(true)}
onKeyUp={() => {
setTimeout(() => {
setIsTyping((t) => t ? false : t)
}, 1000)
}}
onChange={
(e) => typingMessage(e.target.value)
}
placeholder="Type a message here"
/>
<button
onClick={sendMessage}
>send</button>
<pre>{
JSON.stringify({ message, isTyping })
}</pre>
</div>
);
}
I have onChangeName and onChangeAge functions, but I want to make 1 function from these two.
functions onChangeSomeState does not work, I don't no why
codesandbox
import "./styles.css";
import { useState } from "react";
export default function App() {
const [name, setName] = useState("");
const [age, setAge] = useState("");
const onChangeSomeState = (setFunc) => (event) => {
setFunc(event.taget.value);
};
const onChangeName = (e) => {
setName(e.target.value);
};
const onChangeAge = (e) => {
setAge(e.target.value);
};
const onSubmit = () => {
const res = JSON.stringify({
name: name,
age: age
});
console.log(res);
};
console.log(name);
console.log(age);
return (
<div className="App">
<input
onChange={onChangeName}
placeholder="name"
value={name}
className="input"
/>
<input
onChange={() => onChangeSomeState(setAge())}
// onChange={onChangeAge}
placeholder="age"
value={age}
className="input"
/>
<button onClick={onSubmit} type="button" className="button">
submit
</button>
</div>
);
}
I'm not getting any age value in the console
You can just do this:
onChange={onChangeSomeState(setAge)}
When you do onChange={() => onChangeSomeState(setAge())}, you are not passing the event to the handler function.
You can also do:
onChange={(e) => onChangeSomeState(setAge)(e)}
Also there was a typo: setFunc(event.taget.value); missing an r in target
im trying to get input value and push it into state when the person click on submit,
but i confused.
const App = () => {
const [category,setCategory] = useState([])
return (
<div>
<input type="text" name="" id="" />
<button type="submit" >Add</button>
</div>
);
}
export default App;
i tried lot of ways but i coudn't find any solution.
You just need to have another state variable that stores the current input value. Like this:
const [categories, setCategories] = useState([]);
const [category, setCategory] = useState('');
const addCategory = () => {
setCategories([...categories, category]);
// after pushing the value, you may want to reset the input field
setCategory('');
};
...
<input value={category} onChange={(e) => setCategory(e.target.value)} />
<button onClick={addCategory}>Add</button>
Try this
const App = () => {
const [category,setCategory] = useState([])
const addCategory = e => {
const newCategory = category
newCategory.push(e.target.previousElementSibling.value)
setCategory(newCategory)
}
return (
<div>
<input type="text" name="" id="" />
<button type="submit" onClick={addCategory}>Add</button>
</div>
);
}
export default App;
If you don't want to use previousElementSibling then try useRef like this:
const App = () => {
const catRef = useRef(null)
const [category,setCategory] = useState([])
const addCategory = e => {
const newCategory = category
newCategory.push(catRef.current.value)
setCategory(newCategory)
}
return (
<div>
<input type="text" name="" ref={catRef} id="" />
<button type="submit" onClick={addCategory}>Add</button>
</div>
);
}
export default App;
Of course you'll have to import useRef
I have this Component:
export const DashboardPage = () => {
const [mounted, setMounted] = useState(false);
const [currentNumber, setCurrentNumber] = useState(null);
const [insertedNumber, setInsertedNumber] = useState(null);
const [result, setResult] = useState(null);
const [arrayNumbers, setArrayNumbers] = useState([]);
useEffect(() => {
const rnd = randomNumber();
if (!mounted) {
setCurrentNumber(rnd);
setMounted(true);
}
}, [mounted]);
const handleChange = (e) => {
setInsertedNumber(e.target.value);
}
const handleClick = (e) => {
e.preventDefault();
arrayNumbers.push(insertedNumber);
setArrayNumbers(arrayNumbers);
setResult("Oops, ritenta! era " + currentNumber)
if (parseInt(insertedNumber) === parseInt(currentNumber)) {
setResult("EVVIVA hai vinto!");
}
}
const handleOnSubmit = (e) => {
e.preventDefault();
}
return (
<>
<h2>Storico tentativi: {arrayNumbers.map(e => e)}</h2>
<h2>La soluzione è: {currentNumber}</h2>
<Form onSubmit={handleOnSubmit}>
<Form.Group controlId="formGTN">
<Form.Label>Guess the number:</Form.Label>
<Form.Control type="text" onChange={handleChange} />
</Form.Group>
<Button variant="primary" type="submit" onClick={handleClick} label="Is it?" />
</Form>
<h1>{result}</h1>
</>
)
}
export default DashboardPage;
It is a simple "Guess The Number" play.
I have the issue that:
On very first loading of page, I can get into arrayNumbers the only one number I inserted.
From second and other attempts, when I CHANGE the value inside the input, so when I delete previous number, I get state update and I have the previous (and on) numbers. Like if "state" is "back" of one try.
I can see this behavious also in Chrome's Redux plugin. State is update ONLY a step other.
I could move the push into array on onChange, but... If I insert a number of 2 digits I get 2 insert on array (44 will be "4" and "4").
THank you!
You should never mutate the state and/or update the state without setState. In JavaScript, the Array.push() method mutates the original array. So, in your example, you mutate arrayNumbers but you should add a new element to the array without updating the original one.
Also, listening to the onSubmit event is enough, you don't have to add the onClick event to your submit button. You can move everything from handleClick to handleOnSubmit.
export const DashboardPage = () => {
const [mounted, setMounted] = useState(false);
const [currentNumber, setCurrentNumber] = useState(null);
const [insertedNumber, setInsertedNumber] = useState(null);
const [result, setResult] = useState(null);
const [arrayNumbers, setArrayNumbers] = useState([]);
useEffect(() => {
const rnd = randomNumber();
if (!mounted) {
setCurrentNumber(rnd);
setMounted(true);
}
}, [mounted]);
const handleChange = (e) => {
setInsertedNumber(e.target.value);
};
const handleOnSubmit = (e) => {
e.preventDefault();
setArrayNumbers([...arrayNumbers, insertedNumber]);
if (parseInt(insertedNumber, 10) === parseInt(currentNumber, 10)) {
setResult('EVVIVA hai vinto!');
} else {
setResult('Oops, ritenta! era ' + currentNumber);
}
};
return (
<>
<h2>Storico tentativi: {arrayNumbers.map((e) => e)}</h2>
<h2>La soluzione è: {currentNumber}</h2>
<Form onSubmit={handleOnSubmit}>
<Form.Group controlId="formGTN">
<Form.Label>Guess the number:</Form.Label>
<Form.Control type="text" onChange={handleChange} />
</Form.Group>
<Button variant="primary" type="submit" label="Is it?" />
</Form>
<h1>{result}</h1>
</>
);
};
For the above component, you might try these changes in the handleClick function:
const handleClick = (e) => {
e.preventDefault();
let newArr = [...arrayNumbers];
newArr.push(insertedNumber);
setArrayNumbers(newArr);
setResult('Oops, ritenta! era ' + currentNumber);
if (parseInt(insertedNumber) === parseInt(currentNumber)) {
setResult('EVVIVA hai vinto!');
}
};
I'm btrying to save an array of objects in local storage, each time a user clicks a button, i add the username and email fron input fields
but it keeps updating the local storage instead of appending new object to the array
Below is my code
const app = () => {
const [allusers,setAllusers] = useState([JSON.parse(localStorage.getItem('users')) || '']);
const [id,setId] = useState(0);
const [newuser,setNewuser] = useState({
'id':id
'name':'David',
'email':'david#gmail.com'
})
const handleChange = () =>{
setNewuser({...newuser,[e.target.name] : e.target.value});
}
const add = ()=>{
setAllusers([newuser])
localStorage.setItem('users',JSON.stringify(allusers))
setID(id+1); // increase id by 1
}
return(
<div>
<form>
<input type="text" name="user" onChange={handleChange}>
<input type="text" name="email" onChange={handleChange}>
<button onclick={()=>save}>Save</button>
</form>
</div>
)
}
export default app;
There were a lot of syntactical errors and use of functions like save which was never declared and still used.
I rewrote the whole example and made it a bit modular so that you can comprehend it better.
Here is the working example:
Final Output:
Full Source code:
import React, { useState, useEffect } from "react";
import "./style.css";
const App = () => {
const [allusers, setAllusers] = useState([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const handleName = e => {
setName(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const save = e => {
e.preventDefault();
let newUsers = {
id: Math.floor(Math.random() * 100000),
name: name,
email: email
};
localStorage.setItem("users", JSON.stringify([...allusers, newUsers]));
setAllusers(allusers.concat(newUsers));
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
};
useEffect(() => {
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
if (localStorage.getItem("users")) {
setAllusers(JSON.parse(localStorage.getItem("users")));
}
}, []);
return (
<div>
<form>
<input type="text" name="user" onChange={handleName} />
<input type="text" name="email" onChange={handleEmail} />
<button onClick={save}>Save</button>
<p>{JSON.stringify(allusers)}</p>
</form>
</div>
);
};
export default App;
As You inquired in the comment section, here is how you can implement the Update functionality:
Final Output:
Full source code:
import React, { useState, useEffect } from "react";
import "./style.css";
const App = () => {
const [allusers, setAllusers] = useState([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [id, setId] = useState(null);
const handleName = e => {
setName(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const save = e => {
e.preventDefault();
let newUsers = {
id: Math.floor(Math.random() * 100000),
name: name,
email: email
};
localStorage.setItem("users", JSON.stringify([...allusers, newUsers]));
setAllusers(allusers.concat(newUsers));
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
};
const setForUpdate = user => {
setName(user.name);
setEmail(user.email);
setId(user.id);
};
const update = e => {
e.preventDefault();
let modifiedData = allusers.map(user => {
if (user.id === id) {
return { ...user, name: name, email: email };
}
return user;
});
setAllusers(modifiedData);
localStorage.setItem("users", JSON.stringify(modifiedData));
setId(null);
};
useEffect(() => {
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
if (localStorage.getItem("users")) {
setAllusers(JSON.parse(localStorage.getItem("users")));
}
}, []);
return (
<div>
<form>
<input value={name} type="text" name="user" onChange={handleName} />
<input value={email} type="text" name="email" onChange={handleEmail} />
<button disabled={!(id == null)} onClick={save}>
Save
</button>
<button disabled={id == null} onClick={update}>
Update
</button>
</form>
{allusers &&
allusers.map(user => (
<div className="userInfo">
<p>{user.name}</p>
<p>{user.email}</p>
<button onClick={() => setForUpdate(user)}>
select for update
</button>
</div>
))}
</div>
);
};
export default App;
You can find the working example here: Stackblitz
You are trying to save allusers to the localStorage right after setAllUsers() but setState is asynchronous. The value does not have to be updated on the next line. You can read more about it at reactjs.org, Why is setState giving me the wrong value?.
I would recommend to use useEffect.
const add=()=> {
setAllusers([... allusers ,newuser])
}
useEffect(()=>{
// this is called only if the variable `allusers` changes
// because I've specified it in second argument of useEffect
localStorage.setItem('users',JSON.stringify(allusers))
}, [allusers]);
()=>handleChange is a function that takes no arguments and returns the handleChange function. You probably want () => handleChange(), which would take no arguments and INVOKE handleChange.
you are adding only one new user while clicking on add button. You need to copy previous data also when setting all users.
Second thing setting state is async and hence your localStorage and allusers may have different value and to avoid this one you need to use useEffect to set the value.
const add = ()=>{
setAllusers([...allusers ,newuser])
setID(id+1); // increase id by 1
}
useEffect(() => {
localStorage.setItem('users',JSON.stringify(allusers))
},[allusers])