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>
);
}
Related
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
I have form check box where the values of checked boxed are returned as json, if i uncheck the json is still showing the value of the checked value
onChange function
const setApproveDeclineValues = (e) => {
setChecked(!checked);
setIsChecked({ ...isChecked, [e.target.name]: e.target.value });
console.log(isChecked);
}
Form
<Form.Check
type="checkbox"
id={data.schudule_number}
defaultChecked={checked}
name={data.schudule_number}
value={data.schudule_number}
onChange={setApproveDeclineValues}
/>
useState
const [checked, setChecked] = useState(true);
const [isChecked, setIsChecked] = useState({});
output when checked
{11010: "11010", 11040: "11040"}
expected output if unchecked
{11010: "11010"}
And also how do i update the checked values on useEffect(); ? By defualt all the checkbox are selected, how do i get this values ? i am getting empty json
{}
on page load
I don't think it fully solves it, I mean, works, but it's now scoped for each input. You would need some hight order state to gather this data I guess... But maybe would give some idea.
The pieces of code that looks like setState(s => !s) is, what I think its called, a dispatch function. useState receives the current state as a argument that you can use, rather than access the state itself (would cause a react warning or a infinite loop. I broke my chrome.)
here's a codesandbox with the code:
https://codesandbox.io/s/stack-form-check-box-not-updating-state-on-uncheck-muudh [edited to handle multiples inputs]
ps. I never used delete before, not sure how it works, if its safe, etc.
(and also pasted for posterity)
import { useEffect, useRef, useState } from "react";
import "./styles.css";
function Checkbox({ name, setChecks }) {
const [checked, setChecked] = useState(true);
const inputRef = useRef(null);
useEffect(() => {
if (checked) {
setChecks((c) => ({
...c,
[inputRef.current.name]: inputRef.current.value
}));
} else {
setChecks((c) => {
const newC = { ...c };
delete newC[inputRef.current.name]; // but i think that set the value to false may be better. otherwise the problem that you had here may propagate in database or whatever
return newC;
});
}
}, [checked, setChecks]);
return (
<input
ref={inputRef}
type="checkbox"
defaultChecked={checked} //this could be a prop too
id={name}
name={name}
value={name}
onChange={() => setChecked((c) => !c)}
/>
);
}
export default function App() {
const [checks, setChecks] = useState({});
useEffect(() => {
console.log(checks);
}, [checks]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Checkbox name="one" setChecks={setChecks} />
<Checkbox name="two" setChecks={setChecks} />
<Checkbox name="three" setChecks={setChecks} />
</div>
);
}
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
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>
);
}
I'm building a dropdown with suggestions that fetch data from an API. The input from the search bar is being stored using setState and it is updated when i change the value in the text input.
The thing is that I'm not managing to update the users lists from the dropdown each time I enter a new character in the text input. Can I somehow force the component to be rendered every time the props change? Or is there a better approach?
import React, {useState, useEffect} from 'react';
import Dropdown from '../Dropdown/Dropdown';
import './SearchBar.css';
// Component created with arrow function making use of hooks
const SearchBar = (props) => {
const [input, setInput] = useState('');
const [dropdownComponent, updateDropdown] = useState(<Dropdown input={input}/>)
useEffect(() => {updateDropdown(<Dropdown input={input}/>)}, [input])
const onChange = (e) => {
setInput(e.currentTarget.value)
updateDropdown(<Dropdown input={input}/>)
console.log("=(")
}
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
{dropdownComponent}
</div>
)
}
export default SearchBar;
I can't make the problem happen using your code in a simple test, but your onChange does has a problem: It's using input to update the dropdown, but it's not using useCallback to ensure that input isn't stale when it does. Either:
Don't update the dropdown in your onChange, allowing your useEffect callback to do it; or
Use e.target.value instead of input and get rid of the useEffect updating the dropdown; or
Don't memoize the dropdown (e.g., don't put it in state) since you want to update it when the input changes anyway, just render it directly in the JSX
Of those, with what you've shown, #3 is probably the simplest:
const SearchBar = (props) => {
const [input, setInput] = useState('');
const onChange = (e) => {
setInput(e.currentTarget.value);
};
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
<Dropdown input={input}/>
</div>
);
}
Live Example:
const {useState, useEffect} = React;
function Dropdown({input}) {
return <div>Dropdown for "{input}"</div>;
}
const SearchBar = (props) => {
const [input, setInput] = useState('');
const onChange = (e) => {
setInput(e.currentTarget.value);
};
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
<Dropdown input={input}/>
</div>
);
}
ReactDOM.render(<SearchBar />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>