I have an <input/> which defines an onKeyDown prop which is fired when a key is pressed. In the callback, I check if the keycode matches a certain key (in this case, the up or down arrow keys).
I need to notify a sibling component that is in the same render() method as the <input/> whenever one of the matching keys is pressed.
Here is the relevant code:
handleKey(e) {
let keyCode = e.keyCode;
//Handle up arrow press.
if (keyCode == 38) {
console.log('up arrow pressed')
}
//Handle down arrow press.
if (keyCode == 40) {
console.log('down arrow pressed')
}
}
render() {
return (
<div className="chat-input">
{this.state.showCommandPopup && <CommandPopup input={this.state.inputValue}/> }
<form onSubmit={this.handleSubmit.bind(this)}>
<Input onKeyDown={this.handleKey.bind(this)} value={this.state.inputValue} onChange={this.onInputChange.bind(this)} className="chat-input-comp" placeholder="Say something. You can use / to search, and . for commands." action='Send'/>
//....
</form>
</div>
);
}
How can I notify my <CommandPopup/> component whenever the up or down arrow key is pressed?
Step 1.
You need to call setState inside handleKey() method. Once setState is called parent as well as child component will re-render.
handleKey(e) {
let keyCode = e.keyCode;
//Handle up arrow press.
if (keyCode == 38) {
this.setState({keyvalue:keyCode})
console.log('up arrow pressed')
}
//Handle down arrow press.
if (keyCode == 40) {
console.log('down arrow pressed')
this.setState({keyvalue:keyCode})
}
// It will call render() Method
}
Step 2.
Inside CommandPopup component you need to add, componentWillReceiveProps(newProps).
Inside componentWillReceiveProps(newProps), you will get the updated value of input={this.state.inputValue}
componentWillReceiveProps(newProps){
console.log(newProps.keyvalue,'keycode')
}
You just need to raise an event, nothing prevents you to use js events. But I think you are not doing the proper way, since react is not an event base framework, and there is maybe a better solution to this problem using a state container like redux or mobx.
On your CommandPopup component you just need to subscribe to the event.
class CommandPopup extends Component {
componentDidMount() {
window.addEventListener('keyUp', () => {
console.log('Key UP ...');
});
window.addEventListener('keyDown', () => {
console.log('Key DOWN ....');
});
}
render() {
return (
<div>Something ...</div>
);
}
}
And just dispatch the event:
handleKey(e) {
let keyCode = e.keyCode;
//Handle up arrow press.
if (keyCode == 38) {
const ev = new Event('keyUp', { 'bubbles': true, 'cancelable': false });
document.dispatchEvent(ev);
}
//Handle down arrow press.
if (keyCode == 40) {
const ev = new Event('keyDown', { 'bubbles': true, 'cancelable': false });
document.dispatchEvent(ev);
}
}
render() {
return (
<div className="chat-input">
{this.state.showCommandPopup && <CommandPopup input={this.state.inputValue}/> }
<form onSubmit={this.handleSubmit.bind(this)}>
<Input onKeyDown={this.handleKey.bind(this)} value={this.state.inputValue} onChange={this.onInputChange.bind(this)} />
</form>
</div>
);
}
Again maybe you should take a look to mobx computed values, this is the kind of problem they can solve.
Hope this help.
Related
I ma using element ui el-pagination like this
<el-pagination
#size-change="handleChange"
#current-change="CurrentChange"
:current-page.sync="currentPage"
:page-sizes="[50, 100, 150, 300]"
:page-size="pageSize"
popper-class="popper"
layout="sizes, prev, pager, next"
:total="getTotal"
/>
i have the methods defined
CurrentChange(val) {
this.currentPage = val;
},
i have created a mixin called as pagination.js
because this pagination is used at many places, so i added some keyboard events in it like this
document.querySelectorAll('ul.el-pager li.number').forEach((element, index) => {
element.addEventListener('keyup', function (e) {
if (e.key == 'Enter' || e.key == 'Space') {
this.$root.CurrentChange(element.innerHTML);
}
});
})
but i am getting undefined
i tried the console.log(this.$root) and i getting undefined, what i am missing here, i though the root has all the vue can fetch from
this inside the keyup event handler will refer to the element itself, not the component instance, you need to get this value ( as the component instance ) from outside the forEach callback function.
const that = this;
document.querySelectorAll('ul.el-pager li.number').forEach((element, index) => {
element.addEventListener('keyup', function (e) {
if (e.key == 'Enter' || e.key == 'Space') {
that.$root.CurrentChange(element.innerHTML);
}
});
})
I have the following code:
undoButton.onclick = undoFunction;
document.addEventListener("keydown", (e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyZ") {
e.preventDefault();
undoFunction();
}
});
function undoFunction() {
console.log("undo function...");
}
When I click the button, as excepted, the function code runs once, and so does the console.log, but when I use the key stroke, the function is running a multiple times, up to hundreds of so-called loops at some scenarios. Any suggestion why? I tried to used e.repeat = false but had no luck. Thanks!
Use keyup instead. The keydown event triggers as long a key is hold down. keyup only triggers when a key is released.
var undoButton = document.getElementById('undoButton');
undoButton.onclick = undoFunction;
document.addEventListener("keyup", (e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyZ") {
e.preventDefault();
undoFunction();
}
});
function undoFunction() {
console.log("undo function...");
}
<input id="undoButton" type="button" value="Undo" />
I have a input for searchbox. I must make like; Write my words fors search then after i press enter it must need go another page with input value. So i can access that value with query string. So how can i route another page with value of input after i press enter ? Thank you for help! I Just add That codes for catch enter press.
useEffect(() => {
const listener = (event) => {
if (event.code === "Enter" || event.code === "NumpadEnter") {
alert("Enter key was pressed. Run your function.");
event.preventDefault();
}
};
document.addEventListener("keydown", listener);
return () => {
document.removeEventListener("keydown", listener);
};
}, []);
You don't necessarily have to set an event listener, using onKeyDown event handler will also do. Enter key has a code of 13, so we just have to detect that.
Keep your value in a state (here, myValue), detect that you've pressed Enter key (here, using keyPressHandler method), and finally, pass the parameter to your route.
import {useHistory} from "react-router-dom"
function App() {
let history = useHistory();
const [myValue, setMyValue] = useState("");
const handleChange = ({ target: { value } }) => {
setMyValue(value);
};
const keyPressHandler = (e) => {
if (e.which === 13) {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
return (
<div className="App">
<input value={myValue} onKeyDown={keyPressHandler} onChange={handleChange} />
</div>
);
}
UPDATE:
According to MDN Web Docs, e.which is non-standard [Source] and e.keyCode is deprecated [Source], so you should be using e.key instead like:
const keyPressHandler = (e) => {
if (e.key=== 'Enter') {
// alert("You pressed enter!");
history.push("/process/" + myValue);
}
};
Working CodeSandbox Link
I'm working on a login function right now. I put my enterKey function in the input, and my aim is to call the login function when the user press the enter key. It works fine if there's nothing in the input area, however, I find that if I have some characters in the input text area, the function will be called multiple times and give multiple error messages.
For example, if I have N characters in the input, I will receive (N+1) error messages after press the enter key.
Here is my code:
enterKeyPress() {
window.addEventListener("keypress", e => {
if (e.keyCode === 13) {
console.log('enter key pressed!'); // I will receive this msg (N+1) times when there're N characters in the input text area
e.preventDefault();
this.loginUser(); // this is the login function I want to call after press enter key, but just once per press
}
});
}
render() {
return(
<Input
type="password"
placeholder="Password"
onChange={e =>
this.setState({ password: e.target.value })
}
onKeyPress={this.enterKeyPress()}
/>
);
}
Can anyone help me with this?
Event listeners aren't necessary in this case.
First thing, adjust enterKeyPress to not create an event listener. If you haven't bound the function in your constructor, then you can convert enterKeyPress to an arrow function:
enterKeyPress = (e) => {
if (e.keyCode === 13) {
console.log('enter key pressed!');
e.preventDefault();
this.loginUser();
});
}
Converting enterKeyPress to an arrow function is one way to scope the function to the component. Another option is binding the function in your constructor or in your render function, which is well documented elsewhere. If you've already bound the function in your constructor (you haven't included it here), then you can ignore that part.
Second, adjust your onKeyPress prop to pass the function rather than calling it:
<Input
type="password"
placeholder="Password"
onChange={e =>
this.setState({ password: e.target.value })
}
onKeyPress={this.enterKeyPress}
/>
It's also worth noting that there's another general JavaScript mistake here: using an anonymous callback function in your event listener. By using an anonymous function, you're enabling the same function to be added multiple times, since a different function reference is generated each time. This also means that you won't be able to remove it later, since you'll require the function reference to do so.
Again, you don't need an event listener here, but if you did, you should probably define the callback in the component scope, so that you can remove it at some later point. A common pattern for using event listeners is as follows:
handleKeyPress = (e) => {
if (e.keyCode === 13) {
console.log('enter key pressed!');
e.preventDefault();
this.loginUser();
});
}
componentDidMount() {
window.addEventListener("keypress", this.handleKeyPress);
}
componentWillUnmount() {
window.removeEventListener("keypress", this.handleKeyPress);
}
From some quick Googling, I think this might do what you need:
enterKeyPress(e) {
if (e.keyCode === 13) {
console.log('enter key pressed!'); // I will receive this msg (N+1) times when there're N characters in the input text area
e.preventDefault();
this.loginUser(); // this is the login function I want to call after press enter key, but just once per press
});
}
render() {
return(
<Input
type="password"
placeholder="Password"
onChange={e =>
this.setState({ password: e.target.value })
}
onKeyPress={this.enterKeyPress}
/>
);
}
onKeyPress already does what the event listener you're trying to add does, so just pass it the keypress event directly instead.
Like #Ronnie has pointed out in the comments, you are adding a new event listener every time the onKeyPress function is triggered on the component, which causes the problems. Since onKeyPress event already passes the event as an argument (similar to onClick event), you can access the keyCode from there.
You can change your enterKeyPress function to the following:
enterKeyPress(e) {
if (e.keyCode === 13) {
console.log('enter key pressed!');
e.preventDefault();
this.loginUser();
}
}
I'm in the process of building a customised, accessible select input with React.js. I need to have the up and down arrow keys function as the tab key would within the scope of select input's options.
I have a handleKeyPress function on the elements that detects when other keys are pressed (for instance 'Enter' works fine).
Here is an example option:
<li
className="oc-select-field__item"
tabIndex="0"
onClick={handleClick}
onKeyPress={handleKeyPress}
>
...and here is the handleKeyPress function
handleKeyPress = event => {
if (event.key === 40) {
console.log('Down arrow key fired'); // does not fire
}
if (event.key === 'Enter') {
console.log('Enter key fired'); // does fire
}
};
What am I doing wrong that I am not successfully detecting when the down arrow is pressed?
event.which will give you the numeric value of the key.
event.key and event.code will give you a string value.
Try this tool: http://keycode.info
if (event.key === 'ArrowDown') {
console.log('Down arrow key fired');
}
As #devserkan mentioned you should use onKeyDown instead of onKeyPress.
The keydown event is fired when a key is pressed down. Unlike the keypress event, the keydown event is fired for keys that produce a character value and for keys that do not produce a character value.
For Arrow keys, I think you need onKeyDown instead of onKeyPress.
class App extends React.Component {
handleKeyPress = ( event ) => {
if ( event.key === "ArrowDown" ) {
console.log( "Down arrow key fired" ); // does not fire
}
if ( event.key === "Enter" ) {
console.log( "Enter key fired" ); // does fire
}
};
render() {
return (
<div>
<ul>
<li
tabIndex="0"
onClick={this.handleClick}
onKeyDown={this.handleKeyPress}
>Foo
</li>
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>