React Hooks - setState takes two clicks before working - javascript

I have an array of tags that take an input and update when the user presses enter. For some reason, the user needs to press enter twice before anything happens.
const [ tags, setTags ] = useState([]);
const addTag = (inputEvent) => {
if (inputEvent.key === 'Enter') {
setTags([ ...tags, inputEvent.target.value ]);
inputEvent.target.value = '';
}
};
return(
<input
type="text"
placeholder="Press enter"
onKeyUp={(inputEvent) => addTag(inputEvent)}
/>
)

Need more context to be sure. But i would guess this is a classic stale state problem. Instead of setTags([ ...tags, inputEvent.target.value ]), try use the callback function signature:
setTags(tags => [ ...tags, inputEvent.target.value ])

Use the ref to access the current tag from your input field.
Use form so that it is easier to add the tags and listen to Enter submit.
On submit, update your tags, and reset your form using the ref.
import React from "react";
export default function App() {
const [tags, setTags] = React.useState([]);
const inputRef = React.useRef(null);
const formRef = React.useRef(null);
const addTag = (e) => {
e.preventDefault();
setTags([...tags, inputRef.current.value]);
formRef.current.reset();
};
return (
<div>
<p>{tags.join(",")}</p>
<form onSubmit={addTag} ref={formRef}>
<input type="text" ref={inputRef} placeholder="Press enter" />
</form>
</div>
);
}
Note :- It is advisable in React to use ref to access DOM elements and manipulating them.
You could check a working example here -> https://codesandbox.io/s/wispy-microservice-3j455?file=/src/App.js:0-469

Related

React todolist onDoubleclick Input edit value show the current value instead of empty

I'm trying to create a todolist with React which when you double click on one of the to do list that will show input that allow you to edit the value. But I want to show the before value in the input edit when user click on it and the user can erase the before value and change to new and change the value.
I know, my explanation is very bad. I will show the example that I want in here https://todomvc.com/examples/react/#/.
I want to make just like this person todo mvc which when you double click the todolist the edit input value still shown the before value.
import {useState} from 'react';
export default function App() {
const [todo, setTodo] = useState([])
const [input, setInput] = useState('')
const [edit, setEditing]= useState(null)
const [edittext , setEditingText] = useState('')
const InputHandler = (e)=>{
setInput(e.target.value)
}
const SubmitHandler = ()=>{
setTodo([...todo, {text:input, id: Math.random()*1000}])
setInput('')
}
const EditHandler = (e)=>{
setEditingText(e.target.value)
console.log(e.target.value)
}
const SubmitEdit = (id)=>{
setTodo([...todo].map((todos)=>{
if(todos.id === id){
todos.text = edittext
}
return todos
}))
setEditing(null)
setEditingText("")
}
return (
<div className="App">
<input value={input} onChange={InputHandler}/>
<button onClick={SubmitHandler}>Add</button>
{todo.map(todos =>
<div key={todos.id}>
{edit === todos.id ?
(<><input type="text" value={edittext} onChange={EditHandler}/>
<button onClick={()=>SubmitEdit(todos.id)}>Edit</button></>)
: (<p onDoubleClick={()=>setEditing(todos.id)}>{todos.text}</p>)
}
</div>
)}
</div>
);
}
I'm sorry if my explanation is a little confusing.
It's all good, just update the editing text as well on double click.
<p onDoubleClick={()=>{setEditing(todos.id); setEditingText(todos.text)}}>{todos.text}</p>
I get you, first in the input to edit the todo you need to use the same value as what you used in adding todo.
<input type="text" value={todos.text} onChange={EditHandler}/>
But in your todo app you use many state which make an app very hard to manage its state.
Lastly its bad practice to use useState function direct in the eventHandler function like what you have used in onDoubleClick.
Try to visit thinking in React by React js website. You will thanks your self for your time. https://reactjs.org/docs/thinking-in-react.html

Adding data to array using UseState onChange

I am having some issues figuring out how I can get the state of an inputfield, and add it to an useState array.
The way this code is set up, using onChange, it will add every character I type in the textField as a new part of the array, but I dont want to set the value until the user is done typing.
What would be a simple solution to this?
My code:
const [subject, setSubject] = useState([]);`
<input type="text" placeholder={"Eks. 'some example'"} onChange={(e) => setSubject(oldArray => [...oldArray, e.target.value])}/>
Well, I am not confident with react yet, but unless you don't want to do some validation, why don't you use useRef hook and onBlur combination. UseRef hook basically set a reference on element and then you can use value from that reference which in your case would be textField value. OnBlur will trigger when user clicks outside of input (input lose focus) Code should look like this:
import react, {useRef, useState} from "react";
const someComponent = (props) => {
const [subject, setSubject] = useState([]);
const textAreaRef = useRef();
const onBlurHandler = () => {
setSubject((prevSubject) => [...prevSubject, textAreaRef.current.value]);
}
return <input type="text" placeholder={"Eks. 'some example'"} ref={textAreaRef} onBlur={onBlurHandler}/>
}
Other way would be to use debouncing with useEffet.
this is a little something i cooked up for you... it watches the change of the input, and 1 second after the person stops typing, it will add the input value.
The main things to look at here are the useEffect() and the <input /> with the new state i made [input, setInput]. or you can play around with this here
export default function App() {
const [subjects,setSubjects] = useState([]);
const [input,setInput] = useState("")
useEffect(() => {
const timer = setTimeout(() => {
setSubjects(old => [...old, input])
}, 1000)
return () => clearTimeout(timer)
}, [input])
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<input placeholder="type here"
value={input}
type="text"
onChange={e => setInput(e.target.value)}
/>
{subjects.length === 0 ?
<h3>Nothing yet...</h3>
:
<h3>{subjects}</h3>
}
</div>
);
}

Remove object property inside ReactJs element - using spread syntax

I'm triyng to remove an object property using spread syntax rendering a React component. I wonder if there is a way to achieve without adding to much extra code. I'm using {reset,...inputName}
I have a custom hook (useField) for every input in my form. useField also has a reset function for my reset button. But I want to remove the reset property only for the inputs element.
custom hook useField
export const useField = (type) => {
const [value, setValue] = useState('')
const onChange = (event) => {
setValue(event.target.value)
}
const reset = () => {
setValue('')
}
return {
type,
value,
onChange,
reset
}
}
react form
const MyForm = (props) => {
const content = useField('text')
const author = useField('text')
const info = useField('text')
const handleClick = () => {
content.reset()
author.reset()
info.reset()
}
return (
<div>
<h2>create a new anecdote</h2>
<form onSubmit={handleSubmit}>
<div>
content
<input {reset,...content} />
</div>
<div>
author
<input {reset,...author} />
</div>
<div>
url for more info
<input {reset,...info} />
</div>
<button>create</button>
<input type="reset" value='reset' onClick={handleClick} />
</form>
</div>
)
}
For future reference, what may work for OP are changes similar to below:
const { reset: resetContent, ...content} = useField('text')
const { reset: resetAuthor, ...author} = useField('text')
const { rest: resetInfo, ...info} = useField('text')
const handleClick = () => {
resetContent();
resetAuthor();
resetInfo();
};
.
.
.
<div>
content
<input {...content} />
</div>
.
.
.
Explanation
the object returned from useField is destructured
the reset prop is separated and renamed as resetContent (or resetAuthor, resetInfo, as required)
the rest of the props go into the content variable (or author, info variables, as required)
when rendering in the JSX, the content is used
Thus, effectively the reset prop from useField was 'removed' (technically, it was just separated, though) in the new content object.

Adding an editable prefix to the input - react

I want to add an editable prefix to the phone number input. My code looks like this
<input
changeHandler={this.onInputChange}
errors={errors}
onBlur={this.onInputBlur}
onFocus={this.onInputFocus}
resetErrors={clearFieldErrors}
value={`+41${phoneNumber}`}/>
however, after adding the prefix I can't write anything in input. Does anyone know why this is happening and how to fix it?
Instead of changeHandler, you should be using onChange. See examples in
https://reactjs.org/docs/forms.html#controlled-components.
Also you might use it as uncontrolled-component and give defaultValue={'+41'} too.
Instead of passing hard coded "+41" in value prop, just initialize the phoneNumber useState
Try the following code, you may find want you looking for.
export default function App() {
const [inputPhoneNumber, setInputPhoneNumber] = React.useState('');
const [phone, setPhone] = React.useState('+41');
const inputChangeHandler = ({ target }) => {
setPhone(target.value);
};
const submitHandler = (e) => {
e.preventDefault();
setInputPhoneNumber(phone);
setPhone('+41');
};
return (
<>
<form onSubmit={submitHandler}>
<input onChange={inputChangeHandler} value={phone} />
<button type="submit">Submit</button>
</form>
<h1>{inputPhoneNumber}</h1>
</>
);
}

Detecting which input is focused React hooks

I'm using React Hooks. I want to check which input is focused. I have an object of dynamically generated inputs. The inputs will be selected and I want to have a button that will append a value to the input that is in focus.
(Edit) Updated to a much better solution using React Hook
Most solutions I've come across don't take into account when the form has no active element. Hence I came up with the following hook to cover this case.
const useActiveElement = () => {
const [listenersReady, setListenersReady] = React.useState(false); /** Useful when working with autoFocus */
const [activeElement, setActiveElement] = React.useState(document.activeElement);
React.useEffect(() => {
const onFocus = (event) => setActiveElement(event.target);
const onBlur = (event) => setActiveElement(null);
window.addEventListener("focus", onFocus, true);
window.addEventListener("blur", onBlur, true);
setListenersReady(true);
return () => {
window.removeEventListener("focus", onFocus);
window.removeEventListener("blur", onBlur);
};
}, []);
return {
activeElement,
listenersReady
};
};
https://codesandbox.io/s/competent-thunder-59u55?file=/src/Form.js
That should make it easier for you to detect which form input is active.
Try to add events on Focus and on Focus Lost.
W3Schools Reference
<input type="text" onFocus="this.props.onFocus()" onFocusOut="this.props.lostFocus()">
You could use onFocus event.
function handleFocus(e) {
// logic here
}
<input onFocus={handeFocus} />
If you want to change input value, you could also use onChange event and value attribute.
const [value, setValue] = useState('')
function handleChange(e) {
setValue(e.target.value)
}
function handleFocus(e) {
// logic here
setValue('input focused')
}
<input value={value} onChange={handleChange} onFocus={handeFocus} />
Hope this will help :)
import React,{useRef} from "react";
import "./styles.css";
export default function App() {
const inputRef = useRef();
const onButtonClick=()=>{
inputRef.current.focus();
}
return (
<div className="App">
<input type="text" value="" ref={inputRef}/>
<button onClick={onButtonClick}>Focus the input</button>
</div>
);
}

Categories

Resources