What I am trying to do: I want to make it so that when you click on a button with the mouse or with enter from the keyboard that you cannot click on it again afterwards. In other words, disable it after pressing the button one time.
Clicking for the second time with the mouse worked, but I can't get it to work with enter.
My code is as follows.
function HomeFood() {
const [food, setFood] = useState(null);
const [calories, setCalories] = useState('');
const [disabled, setDisabled] = useState(false);
async function foodData() {
const result = await axios.get(`link`);
console.log('DATA RESULTS:', result.data);
setFood(result.data)
}
function handleChange(e) {
setCalories(e.target.value);
}
function handleKeyPress(e) {
if (e.key === 'Enter') {
foodData()
}
}
return (
<div className="container">
<section className='food'>
<input
type='number'
onChange={handleChange}
onKeyPress={handleKeyPress}
/>
<button
onClick={() => {
foodData();
setDisabled(true);
}}
disabled={disabled}
>
Click here
</button>
</section>
{food && <FoodNutrients mealsData={food}/>}
</div>
);
}
I tried to use the disabled state in the onKeyPress function, but only get errors. I also tried putting it in the input of the onKeyPress, but again more errors.
Just like the onClick, invoke setDisabled in handleKeyPress:
function handleKeyPress(e) {
if (e.key === 'Enter') {
if (disabled) { // do nothing if already disabled
return;
}
foodData();
setDisabled(true);
}
}
Add a second disabled state, i.e. inputDisabled and toggle it on the same way. This allows each to disabled independently of the other.
function HomeFood() {
const [food, setFood] = useState(null);
const [calories, setCalories] = useState('');
const [disabled, setDisabled] = useState(false);
const [inputDisabled, setInputDisabled] = useState(false); // <-- new state
async function foodData() {
const result = await axios.get(`link`);
console.log('DATA RESULTS:', result.data);
setFood(result.data)
}
function handleChange(e) {
setCalories(e.target.value);
}
function handleKeyPress(e) {
if (e.key === 'Enter') {
foodData();
setInputDisabled(true); // <-- set input disabled
}
}
return (
<div className="container">
<section className='food'>
<input
type='number'
onChange={handleChange}
onKeyPress={handleKeyPress}
disabled={inputDisabled} // <-- attach disabled prop
/>
<button
onClick={() => {
foodData();
setDisabled(true);
}}
disabled={disabled}
>
Click here
</button>
</section>
{food && <FoodNutrients mealsData={food}/>}
</div>
);
}
If you wanted them to be disabled together at the same time then just use the same disabled state and setter for both.
Related
I am using the Modal Component from React Bootstrap Everything works as expected, except from the fact that I cannot figure out how to auto close the Modal after a successful submit.
I know I am able to call the onHide fuction from the Modal component like this at button click:
<Button onClick={props.onHide}>Close</Button>
Is there a way to auto call this onHide function if, and only if there is a successful submit form the MailingListSendgrid component?
index.js
<ModalMailingList show={modalShow} onHide={() => setModalShow(false)} />
ModalMailingList.js
import Modal from "react-bootstrap/Modal";
import MailingListSendgrid from "#/components/MailingListSendgrid";
export default function ModalMailingList(props) {
return (
<Modal
{...props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
className="special_modal" //Add class name here
>
<Modal.Header closeButton></Modal.Header>
<Modal.Body>
<MailingListSendgrid />
</Modal.Body>
</Modal>
);
}
MailingListSendgrid.js
.
.
.
.
const MailingListSendgrid = () => {
const [isError, setIsError] = useState(true);
const [shakeIt, setshakeIt] = useState(false);
const [mail, setMail] = useState("");
const [isLoading, setLoading] = useState(false);
const [message, setMessage] = useState(null);
const subscribe = () => {
const regEx = /[a-zA-Z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,8}(.[a-z{2,8}])?/g;
setMail("");
if (!regEx.test(mail) && mail !== "") {
setIsError(true);
setshakeIt(true);
setMessage("Email is Not Valid");
setTimeout(() => {
setshakeIt(false);
}, 1000);
} else if (mail === "") {
setIsError(true);
setshakeIt(true);
setMessage("Email is Empty");
setTimeout(() => {
setshakeIt(false);
}, 1000);
} else {
setLoading(true);
axios
.put("api/MailingList", {
mail,
})
.then((result) => {
if (result.status === 200) {
setIsError(false);
setMessage(result.data.message);
setLoading(false);
}
})
.catch((err) => {
setIsError(true);
setMessage(err.data.message);
setLoading(false);
});
setMessage(null);
setshakeIt(false);
}
};
return (
.
.
.
<input
onChange={(e) => {
setMail(e.target.value);
}}
type="email"
className={`form-control required email w-auto text-center text-sm-start`}
placeholder={subscription.formPlaceholder}
value={mail}
autoComplete="email"
required
></input>
<button
type="submit"
name="subscribe"
onClick={subscribe}
className="input-group-text justify-content-center"
disabled={isLoading}
>
.
.
.
);
};
export default MailingListSendgrid;
I think that the best option here is to pass a function as a prop, thus still making the MailingListSendgrid reusable e.g.
<MailingListSendgrid onSubmit={()=> props.onHide()} />
And just use that in MailingListSendgrid if it was successfull.
I want to disable the submit button on the form if the user enters a value that already exists in the task list.
'Todos' is the array that stores the list and I used 'some' to check if the input value is equal to some title. However, it doesn't work.
CODE
const [disable, setDisable] = useState(false);
const onFormSubimit = (event) => {
event.preventDefault();
if (!editTodo) { //mudar depois a condição
setTodos([...todos, {
id: uuidv4(),
title: input,
completed: false
}]);
setInput("");
} else {
updateTodo(input, editTodo.id, editTodo.completed);
}
}
const handleInputChange = (event) => {
let inputValue = event.target.value;
setInput(inputValue);
getInputValue(inputValue);
}
const getInputValue = (inputValue) => {
let some = todos.some(item => item.title === inputValue);
if (some!=disable) {
setDisable(true);
}
}
return (
<form onSubmit={onFormSubimit}>
<input type='text' name='text' placeholder='Insira o nome da tarefa' className='task-input' value={input} onChange={handleInputChange} required></input>
<button type='submit' className='button-add' disabled={getInputValue} >{editTodo ? 'Alterar' : 'Adicionar'}</button>
</form>
);
}
Your button state is dependent on input (the variable set by setInput) and todos. So the best way to handle this is via an useEffect with a dependency array.
useEffect(() => {
// Array.some returns a boolean
setDisable(todos.some(item => item.title === input))
}, [todos, input]);
Your button code
<button type='submit' className='button-add' disabled={disable} >{editTodo ? 'Alterar' : 'Adicionar'}</button>
You can also directly use this logic in button like below. In this case there is no need of useEffect
<button type='submit' className='button-add' disabled={todos.some(item => item.title === input)} >{editTodo ? 'Alterar' : 'Adicionar'}</button>
First, change your button with the code below (as it should be disabled ={disable}, not disabled={getInputValue}). And getInputValue as following.
<button type='submit' className='button-add' disabled={disable} >{editTodo ? 'Alterar' : 'Adicionar'}</button>
const getInputValue = (inputValue) => {
if (todos.some((item) => item.title === inputValue)) {
setDisable(true);
} else {
setDisable(false);
}
};
Application:
Search bar with two text input fields (input1, input2)
Three buttons: SearchX, SearchY, Clear results
Both the searches can take input1 and input2 as parameters to give two different results.
There's a result component which takes both the inputs, action and renders the search component depending on the action.
function TestComponent() {
const [input1, setInput1] = useState('');
const [input2, setInput2] = useState('');
const [action, setAction] = useState(null);
const onInput1Change = evt => setInput1(evt.target.value);
const onInput2Change = evt => setInput2(evt.target.value);
return (
<div>
<input type="text" value={input1} onChange={onInput1Change} />
<input type="text" value={input2} onChange={onInput2Change} />
<button type="button" onClick={() => setAction('SearchX')}>
SearchX
</button>
<button type="button" onClick={() => setAction('SearchY')}>
SearchY
</button>
<button type="button" onClick={() => setAction('Clear results')}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
function ResultComponent({ input1, input2, action }) {
if (action === 'SearchX') {
return <SearchX input1={input1} input2={input2} />;
}
if (action === 'SearchY') {
return <SearchY input1={input1} input2={input2} />;
}
if (action === 'Clear results') {
return null;
}
return null;
}
function SearchX({ input1, input2 }) {
const [result, setResult] = useState(null);
useEffect(() => {
// Fetch and process X-way to get the result. Using timeout to simulate that
const id = window.setTimeout(() => setResult(`Search X result with inputs: ${input1}, ${input2}`), 3000);
return () => window.clearInterval(id);
}, [input1, input2]);
return <div>{result}</div>;
}
function SearchY({ input1, input2 }) {
const [result, setResult] = useState(null);
useEffect(() => {
// Fetch and process Y-way to get the result. Using timeout to simulate that
const id = window.setTimeout(() => setResult(`Search Y result with inputs: ${input1}, ${input2}`), 3000);
return () => window.clearInterval(id);
}, [input1, input2]);
return <div>{result}</div>;
}
ReactDOM.render(<TestComponent />, document.getElementById('root'));
Problem:
We want the search to initiate only when a button is clicked. With below code, after the first search result, as soon as you change your input, the result component expectedly re-renders thereby initiating search again without button click
Steps to reproduce the problem:
Enter "input1" in first text box
Enter "input2" in second text box
Hit on "SearchX"
After 3 seconds you should see something like "Search X result with inputs: input1, input2"
Change any of the input boxes. Need not press enter.
After 3 seconds, the result would change without button click
Possible option:
Planning to use React.memo hook to compare action prop before updating the result component. Action prop can only change on button clicks and hence can solve the problem.
Question:
Is there any other way (any other hooks etc.) to solve the problem?
Or is there any other process/design that I can follow to avoid memo ?
You could, upon input interaction, reset the action back to null. This will clear out the current result and not trigger a "search".
function TestComponent() {
const [input1, setInput1] = useState('');
const [input2, setInput2] = useState('');
const [action, setAction] = useState(null);
const onInput1Change = evt => {
setInput1(evt.target.value);
setAction(null);
};
const onInput2Change = evt => {
setInput2(evt.target.value)
setAction(null);
};
return (
<div>
<input type="text" value={input1} onChange={onInput1Change} />
<input type="text" value={input2} onChange={onInput2Change} />
<button type="button" onClick={() => setAction('SearchX')}>
SearchX
</button>
<button type="button" onClick={() => setAction('SearchY')}>
SearchY
</button>
<button type="button" onClick={() => setAction(null)}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
EDIT Use html5 forms to save input and set action upon submit. When inputs are interacted with the inputs in state aren't updated until form is submitted.
function TestComponent() {
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
const [action, setAction] = useState(null);
return (
<div>
<form
id="searchX"
onSubmit={e => {
e.preventDefault();
setInput1(e.target.inputX.value);
setAction("SearchX");
}}
/>
<form
id="searchY"
onSubmit={e => {
e.preventDefault();
setInput2(e.target.inputY.value);
setAction("SearchY");
}}
/>
<input id="inputX" form="searchX" type="text" />
<input id="inputY" form="searchY" type="text" />
<input form="searchX" type="submit" value="SearchX" />
<input form="searchY" type="submit" value="SearchY" />
<button type="button" onClick={() => setAction(null)}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
Also, setting the "clear results" button action back to null saves a conditional check in ResultComponent, which simplifies to:
function ResultComponent({ input1, input2, action }) {
if (action === 'SearchX') {
return <SearchX input1={input1} input2={input2} />;
}
if (action === 'SearchY') {
return <SearchY input1={input1} input2={input2} />;
}
return null;
}
You can use refs to inputs and only update state on button click.
export default function TestComponent() {
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
const [action, setAction] = useState(null);
const input1Ref = useRef(null);
const input2Ref = useRef(null);
const onButtonClick = () => {
if (input1Ref.current) {
setInput1(input1Ref.current.value);
}
if (input2Ref.current) {
setInput2(input2Ref.current.value);
}
};
const onSearchXClick = () => {
onButtonClick();
setAction("SearchX");
};
const onSearchYClick = () => {
onButtonClick();
setAction("SearchX");
};
return (
<div>
<input ref={input1Ref} type="text" />
<input ref={input2Ref} type="text" />
<button type="button" onClick={onSearchXClick}>
SearchX
</button>
<button type="button" onClick={onSearchYClick}>
SearchY
</button>
<button type="button" onClick={() => setAction("Clear results")}>
Clear results
</button>
<ResultComponent input1={input1} input2={input2} action={action} />
</div>
);
}
I'm constructing an input field in React that looks like so:
When the 'x' is clicked (StyledCloseCircle), the text will be cleared, and the 'x' symbol should disappear. The 'x' symbol is currently shown with javascript when the input field is focused,
export const Search = React.forwardRef((props, ref) => {
const [isFocused, setFocus] = useState(false);
const [isHovered, setHover] = useState(false);
return (
<InputContainer
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<StyledInput
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
isHovered={isHovered}
ref={ref}
{...props}
/>
{isFocused && !props.value && (
<StyledMagnifyingGlass
isHovered={isHovered}
isFocused={isFocused}
onClick={props.onSearch}
/>
)}
{isFocused && props.value && (
<StyledCloseCircle onClick={() => console.log("THIS DOES NOT FIRE")} />
)}
{!isFocused && (
<StyledMagnifyingGlass
isHovered={isHovered}
isFocused={isFocused}
onClick={props.onSearch}
/>
)}
</InputContainer>
);
});
The issue is that when the 'x' is clicked, the input looses focus, which causes the 'x' to be removed on the next render, and does not register the click event. It does, however, trigger the mousedown event.
Therefore, my two questions are:
What is the order of operations when the 'x' is clicked, that leads it to registering mousedown but not click?
How can I achieve the desired behavior?
You should create a separate state to control where to show/hide the Clear button. Show it onFocus even as you do now but hide it if user clicks outside of the input container or if clicks on the Clear button. You can additionally hide it onBlur but with some timeout (500-1000ms) in case if user uses keyboard instead of a mouse.
This is a CodeSnadbox example of the code below:
function App() {
const inputContainerRef = useRef();
const [value, setValue] = useState("");
const [showClear, setShowClear] = useState(false);
const onFocus = useCallback(() => {
setShowClear(true);
}, []);
const onClear = useCallback(() => {
setValue("");
setShowClear(false);
}, []);
const onOutsideClick = useCallback(e => {
// hide Clear button only if clicked outside of the container
if (!inputContainerRef.current.contains(e.target)) {
setShowClear(false);
}
}, []);
useLayoutEffect(
() => {
// set the listener only if we shown the Clear button and remove the listener once we hid it
if (showClear) {
document.addEventListener("click", onOutsideClick);
return () => document.removeEventListener("click", onOutsideClick);
}
},
[showClear] // re-invoke if the state changes
);
return (
<div className="App">
<div className="input-container" ref={inputContainerRef}>
<input
value={value}
onChange={e => {
setValue(e.target.value);
}}
className="input"
type="tetxt"
onFocus={onFocus}
/>
{showClear && (
<div className="clear" onClick={onClear}>
X
</div>
)}
</div>
</div>
);
}
Here is my form and the onClick method. I would like to execute this method when the Enter button of keyboard is pressed. How ?
N.B: No jquery is appreciated.
comment: function (e) {
e.preventDefault();
this.props.comment({
comment: this.refs.text.getDOMNode().value,
userPostId:this.refs.userPostId.getDOMNode().value,
})
},
<form className="commentForm">
<textarea rows="2" cols="110" placeholder="****Comment Here****" ref="text" /><br />
<input type="text" placeholder="userPostId" ref="userPostId" /> <br />
<button type="button" className="btn btn-success" onClick={this.comment}>Comment</button>
</form>
Change <button type="button" to <button type="submit". Remove the onClick. Instead do <form className="commentForm" onSubmit={onFormSubmit}>. This should catch clicking the button and pressing the return key.
const onFormSubmit = e => {
e.preventDefault();
// send state to server with e.g. `window.fetch`
}
...
<form onSubmit={onFormSubmit}>
...
<button type="submit">Submit</button>
</form>
It's been quite a few years since this question was last answered.
React introduced "Hooks" back in 2017, and "keyCode" has been deprecated.
Now we can write this:
useEffect(() => {
const listener = event => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
console.log("Enter key was pressed. Run your function.");
event.preventDefault();
// callMyFunction();
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
}, []);
This registers a listener on the keydown event, when the component is loaded for the first time. It removes the event listener when the component is destroyed.
Use keydown event to do it:
input: HTMLDivElement | null = null;
onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
// 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
this.onSubmit();
}
}
onSubmit = (): void => {
if (input.textContent) {
this.props.onSubmit(input.textContent);
input.focus();
input.textContent = '';
}
}
render() {
return (
<form className="commentForm">
<input
className="comment-input"
aria-multiline="true"
role="textbox"
contentEditable={true}
onKeyDown={this.onKeyDown}
ref={node => this.input = node}
/>
<button type="button" className="btn btn-success" onClick={this.onSubmit}>Comment</button>
</form>
);
}
this is how you do it if you want to listen for the "Enter" key.
There is an onKeydown prop that you can use and you can read about it in react doc
and here is a codeSandbox
const App = () => {
const something=(event)=> {
if (event.keyCode === 13) {
console.log('enter')
}
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<input type='text' onKeyDown={(e) => something(e) }/>
</div>
);
}
import React, { useEffect, useRef } from 'react';
function Example() {
let inp = useRef();
useEffect(() => {
if (!inp && !inp.current) return;
inp.current.focus();
return () => inp = null;
});
const handleSubmit = () => {
//...
}
return (
<form
onSubmit={e => {
e.preventDefault();
handleSubmit(e);
}}
>
<input
name="fakename"
defaultValue="...."
ref={inp}
type="radio"
style={{
position: "absolute",
opacity: 0
}}
/>
<button type="submit">
submit
</button>
</form>
)
}
Enter code here sometimes in popups it would not work to binding just a form and passing the onSubmit to the form because form may not have any input.
In this case if you bind the event to the document by doing document.addEventListener it will cause problem in another parts of the application.
For solving this issue we should wrap a form and should put a input with what is hidden by css, then you focus on that input by ref it will be work correctly.
If you don't have the form inside <form>, you could use this in componentDidMount():
componentDidMount = () => {
document.addEventListener("keydown", (e) =>
e.code === "Enter" && console.log("my function"))
}
componentDidMount() //<-- remove this, it's just for testing here
useEffect(() => {
const keyEnter = event => {
if (event.key === 'Enter') {
event.preventDefault()
}
}
document.addEventListener('keydown', keyEnter)
return () => {
document.removeEventListener('keydown', keyEnter)
}
}, [])
I've built up on #user1032613's answer and on this answer and created a "on press enter click element with querystring" hook. enjoy!
const { useEffect } = require("react");
const useEnterKeyListener = ({ querySelectorToExecuteClick }) => {
useEffect(() => {
//https://stackoverflow.com/a/59147255/828184
const listener = (event) => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
handlePressEnter();
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
}, []);
const handlePressEnter = () => {
//https://stackoverflow.com/a/54316368/828184
const mouseClickEvents = ["mousedown", "click", "mouseup"];
function simulateMouseClick(element) {
mouseClickEvents.forEach((mouseEventType) =>
element.dispatchEvent(
new MouseEvent(mouseEventType, {
view: window,
bubbles: true,
cancelable: true,
buttons: 1,
})
)
);
}
var element = document.querySelector(querySelectorToExecuteClick);
simulateMouseClick(element);
};
};
export default useEnterKeyListener;
This is how you use it:
useEnterKeyListener({
querySelectorToExecuteClick: "#submitButton",
});
https://codesandbox.io/s/useenterkeylistener-fxyvl?file=/src/App.js:399-407
I have found this to be easier.
Listen for the keyDown event on the input you want to submit by pressing 'Enter" key and handle the submit action with conditional ternary operator as show below in a single line.
This is mostly used on subscribing a newsletter where there's no need of a button to submit.
Hope it helps.
<input
type="email"
placeholder="Email"
onKeyDown={e => e.key === 'Enter' ? handleSubmit : ''} />
You can use <button type='submit'></button> with nothing in the middle.
here is very optimised code
useEffect(() => {
document
.getElementById("Your-element-id")
.addEventListener("keydown", function (event) {
if (event.code === "Enter" || event.code === "NumpadEnter") {
event.preventDefault();
document.getElementById("submit-element").click();
}
});
}, []);
use mousetrap
https://www.npmjs.com/package/mousetrap
https://www.npmjs.com/package/#types/mousetrap
(yeah, I know, unfortunatelly when You use typescript u have to install types aside from basic module)
import {bind} from 'mousetrap';
const handleSubmit = async () => {
// submit func here
};
bind(['enter', 'return'], handleSubmit);
other example of using mousetrap, maybe for other purpose:
bind(['command+k', 'ctrl+k'], function(e) {
highlight([11, 12, 13, 14]);
return false;
});
So, I was looking for some solution around the same scenario where on the login page, after a user hits(press) enter button from keyboard should trigger login process.
You can configure the textbox with one of code,
<input
// rest your code
onKeyPress={ onkeyup }
/>
Please keep in mind I am using react hooks to achieve it, apart from that this link will help you understand more enter key event handler
Try this enter code here:
const enterKye=()=>{
if(e.key==="Enter"){
alert("hello");
}
}
<input type="text" onKeyPress={enterKye}>
You may approach this problem like this.
onKeyPress={e => e.key === 'Enter' && handleFormSubmit}
You can change only button type => button to submit
<form
onSubmit={e => {
e.preventDefault();
handleSubmit(e);
}}
>
<input
name="developers"
defaultValue="submit"
ref={dev}
type="radio"
/>
<button type="submit">
submit
</button>
</form>
for example next React+TS code(add use hooks for state and etc):
type Props = {
...any properties
} & [any other type if need]
//I want notice that input data type of component maybe difference from type of props
const ExampleComponent: React.FC<Props> = (props: [Props or any other type]){
const anySerice = new AnyService();
const handleSubmit = async (eventForm) => {
await anySerice.signUp();
}
const onKeyUp = (event: KeyboardEvent) => {
//you can stay first condition only
if (event.key === 'Enter' || event.charCode === 13) {
handleSubmit(event)
}
}
...other code
return (<Form noValidate validated={validated} className="modal-form-uthorize" onKeyPress={onKeyUp}>
...other components form
</Form>)
}
export default ExampleComponent;
I solved this problem by sent autoFocus property in button
<button autoFocus={true}></button>