React: Remove focus from input element on escape key - reactjs

I have an input tag, when I press the escape key, I want the focus on the input element to go away, meaning there is no longer a text cursor or any focus styling(when the user types, their input will not go into the input tag).
The code below is my attempt at implementing the functionality described above, but it does not work as intended. I would like to get this code to work as intended.
import React, { useRef } from 'react';
const onEscape = function (action) {
window && window.addEventListener('keydown', (e) => {
if (e.key === "Escape") {
action();
};
});
};
const MyComponent = () => {
...
const descRef = useRef();
onEscape(() => {
descRef.blur();
});
return (
<div>
<input
ref={descRef}
...
/>
</div>
);
};
I have tested that onEscape function and it works when other actions are passed to it(when the user presses the escape key, it executes whatever function has been passed to the onEscape function)

Just from glancing at your code all you need is to add the current property to the ref
Try this
onEscape(() => {
descRef.current.blur();
});

Related

React confirm alert handle key press = enter

import { confirmAlert } from "react-confirm-alert"; // Import
import "react-confirm-alert/src/react-confirm-alert.css"; // Import css
export default function App() {
const handleButtonPress = (event) => {
if (event.key === "Enter") {
alert("Click Yes");
}
};
const handleClick = (e) => {
confirmAlert({
title: "Confirm to submit",
message: "Are you sure to do this.",
buttons: [
{
label: "Yes",
onClick: () => {
alert("Click Yes");
},
onKeyPress: () => {
handleButtonPress();
}
},
{
label: "No",
onClick: () => {
alert("Click No");
}
}
]
});
};
return (
<div className="App">
<button
onClick={() => {
handleClick();
}}
>
Click
</button>
</div>
);
}
I'm testing react-confirm-alert.
I'm trying to handle Button Yes by pressing Enter. Both function onClick() for yes and no are working good, but press enter is not working.
Can someone let me know if I did something wrong?
There's several issues. This react-confirm-alert library looks like a poor library, I'd pick a different one if you can find one that suits your needs.
You're calling handleButtonPress() with no arguments, and then you're trying to read from the event object, which you don't pass.
const handleButtonPress = (event) => {
if (event.key === "Enter") {
The event.key line should be throwing an error since event is undefined. Since you're not seeing errors, it's clear this line isn't getting called. You should be using console.log or the debugger to double check what code is getting called.
You should also get in the habit of reading documentation. In this case, the documentation shows that onKeyPress is a top level setting, while you've incorrectly put it in buttons.
Either way, react-confirm-alert doesn't pass the event to the onKeyPress callback: https://github.com/GA-MO/react-confirm-alert/#options so it doesn't seem like this API should exist. It doesn't have any use.
I would either pick a different library, otherwise you'll need to add your own keypress listener to the document, and manually handle the enter key there.

clickOutside hook triggers on inside select

I have a card component which consists of 2 selects and a button, select1 is always shown and select2 is invisible until you press the button changing the state. I also have an onClickOutside hook that reverts the state and hides select2 when you click outside the card.
The problem Im having is that in the case when select2 is visible, if you use any select and click on an option it registers as a click outside the card and hides select2, how can I fix this?
Heres the relevant code from my card component:
const divRef = useRef() as React.MutableRefObject<HTMLInputElement>;
const [disableSelect2, setDisableSelect2] = useState(true);
const handleActionButtonClick = () => {
setDisableSelect2(!disableSelect2)
}
useOutsideClick(divRef, () => {
if (!disableSelect2) {
setDisableSelect2(!disableSelect2);
}
});
return (
<div ref={divRef}>
<Card>
<Select1>[options]</Select1>
!disableSelect2 ?
<Select2>[options]</Select2>
: null
<div
className="d-c_r_action-button"
onClick={handleActionButtonClick}
>
</Card>
</div>
);
};
And this is my useoutsideClick hook
const useOutsideClick = (ref:React.MutableRefObject<HTMLInputElement>, callback:any) => {
const handleClick = (e:any) => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
useEffect(() => {
document.addEventListener("click", handleClick);
return () => {
document.removeEventListener("click", handleClick);
};
});
};
Extra informtaion: Im using customized antd components and cant use MaterialUI
I tried to recreate your case from the code you shared. But the version I 'built' works.
Perhaps you can make it fail by adding in other special features from your case and then raise the issue again, or perhaps you could use the working code from there to fix yours?
See the draft of your problem I made at https://codesandbox.io/s/serverless-dust-njw0f?file=/src/Component.tsx

How to call react function from external JavaScript file

I have read this post [ https://brettdewoody.com/accessing-component-methods-and-state-from-outside-react/ ]
But I don't understand.
that is not working on my source code.
it's my tsx file
declare global {
interface Window {
fn_test(): void;
childComponent: HTMLDivElement; <-- what the... ref type????
}
}
export default function Contact(): React.ReactElement {
....
function file_input_html( i:number ): React.ReactElement {
return (
<form id={`frm_write_file_${i}`} .... </form>
)
}
....
return (
<div ref={(childComponent) => {window.childComponent = childComponent}}>
....
)
it's my external javascript file
function fn_test(){
window.childComponent.file_input_html(3)
var element = document.getElementById("ifrm_write_file");
// element.value = "mystyle";
}
How can i call file_input_html function?
plase help me ...
You have some logic here that doesn't completely make sense.
In your class, you define file_input_html, which returns a component.
Then, in fn_test, you call attempt to call that function (which doesn't work -- I'll address that in a minute), but you don't do anything with the output.
The article that you linked to tells you how to get a ref to a component (eg the div in this case) -- not the actual Contact, which doesn't have a property named file_input_html anyway -- that's just a function you declared inside its scope.
What I'm assuming you want to happen (based on the code you shared) is for your external javascript file to be able to tell your component to render a form with a certain ID and then be able to get a reference to it. Here's an example of how to do this (it's a little convoluted, but it's a funny situation):
const { useState } = React
const App = (props) => {
const [formId, setFormId] = useState(2)
useEffect(() => {
window.alterFormId = setFormId
},[])
return (<div id={"form" + formId} ref={(ourComponent) => {window.ourComponent = ourComponent}}>
Text {formId}
</div>);
}
setTimeout(() => {
window.alterFormId(8);
setTimeout(() => {
console.log(window.ourComponent)
window.ourComponent.innerText = "Test"
}, 20)
}, 1000)
ReactDOM.render(<App />,
document.getElementById("root"))
What's happening here:
In useEffect, I set alterFormId on window so that it can be used outside of the React files
Using the technique you linked to, I get a ref to the div that's created. Note that I'm setting the ID here as well, based on the state of formId
The setTimeout function at the end tests all this:
a) It waits until the first render (the first setTimeout) and then calls alterFormId
b) Then, it waits again (just 20ms) so that the next run loop has happened and the React component has re-rendered, with the new formId and reference
c) From there, it calls a method on the div just to prove that the reference works.
I'm not exactly sure of your use case for all this and there are probably easier ways to architect things to avoid these issues, but this should get you started.
안녕하세요. 자바스크립트로 흐름만 알려드리겠습니다
아래 코드들을 참고해보세요.
iframe간 통신은
window.postMessage API와
window.addEventListener('message', handler) 메시지 수신 이벤트 리스너 로 구현할 수있습니다. 보안관련해서도 방어로직이 몇줄 필요합니다(origin 체크 등)
in parent
import React from 'react';
export function Parent () {
const childRef = React.useRef(null);
const handleMessage = (ev) => {
// 방어로직들
if (check ev.origin, check ev.source, ....) {
return false;
}
console.log('handleMessage(parent)', ev)
}
React.useEffect(() => {
window.addEventListener('message', handleMessage);
// clean memory
return () => {
window.removeEventListener('message', handleMessage);
}
})
return (
<div>
<iframe ref="childRef" src="child_src" id="iframe"></iframe>
</div>
)
}
in child
import React from 'react';
export function Iframe () {
const handleMessage = (ev) => {
console.log('handleMessage(child)', ev)
}
const messagePush = () => {
window.parent.postMessage({ foo: 'bar' }, 'host:port')
}
React.useEffect(() => {
window.addEventListener('message', handleMessage);
// clean memory
return () => {
window.removeEventListener('message', handleMessage);
}
})
return (
<div>
<button onClick={messagePush}>Push message</button>
</div>
)
}

input onClick and onFocus collision

I have an <input> element, and I want to show content (div) below the input if it has focus.
Also, on every click on the input, I want to hide/show the div.
The problem is that if the input is not focused, than clicking the input will trigger both onClick and onFocus events, so onFocusHandler will run first so the menu will appear but immidately after that onClickHandler will run and hide it.
This is the code (that doesn't work):
import React from 'react';
const MyComp = props => {
/*
...
state: focused, showContent (booleans)
...
*/
const onFocusHandler = () => {
showContent(true);
setFocused(true);
}
const onClickHandler = () => {
if (focused) {
showContent(false);
} else {
showContent(true);
}
}
return (
<>
<input
onFocus={onFocusHandler}
onClick={onClickHandler}
/>
{
showContent &&
<div>MyContent</div>
}
</>
);
};
export default MyComp;
How can I solve this issue?
This is a very odd desired behavior as an active toggle is rather opposed to an element being focused or not. At first I couldn't see any clear way to achieve the behavior you desire. But I thought it could be achieved.
Use the onMouseDown handler instead of the onClick handler.
Use onFocus handler to toggle on the extra content.
Use onBlur handler to toggle off the extra content.
Code:
const MyComp = (props) => {
const [showContent, setShowContent] = useState(false);
const onFocusHandler = () => {
setShowContent(true);
};
const onBlurHandler = () => {
setShowContent(false);
};
const onClickHandler = () => {
setShowContent((show) => !show);
};
return (
<>
<input
onBlur={onBlurHandler}
onFocus={onFocusHandler}
onMouseDown={onClickHandler}
/>
{showContent && <div>MyContent</div>}
</>
);
};
Note: It should be noted that onMouseDown isn't a true replacement to onClick. With a click event the mouse down and up have to occur in the same element, so if. user accidentally clicks into the input but then drags out and releases, the onClick event should not fire. This may be ok though since the focus event fires before the click event so the input may get focus and toggle the content on anyway. Maybe this quirk is acceptable for your use-case.
You should you onBlur event to setContent to false. There is no need to use the onClick handler for that.

event from onSelect returning null

So I have this weird problem (and I am sorry a newbie on this still) but I have a dropdown list that I want to be able to select from and pass back to a form to submit. it works fine when I do the drop down line items manually, but when I retrieve it from the backend and then map, and try to handle the onSelect but the event on onSelect keeps returning null - so confused - do you see anything obvious in this code?
import React, { useEffect, useState } from 'react';
import { useHttpClient } from '../hooks/http-hook';
import { validate } from '../util/validators';
import 'bootstrap/dist/css/bootstrap.min.css';
import DropdownButton from 'react-bootstrap/DropdownButton';
import Dropdown from 'react-bootstrap/Dropdown';
import './Input.css';
const Select = props => {
console.log('props.id=' + props.id);
console.log('props.label=' + props.label);
const [selValue, setSelValue] = useState('');
const { isLoading, error, sendRequest, clearError } = useHttpClient();
const [loadedFoodgroups, setLoadedFoodgroups] = useState([]);
useEffect(() => {
const fetchFoodgroups = async () => {
try {
const responseData = await sendRequest('http://localhost:5000/api/foodgroups')
setLoadedFoodgroups(responseData);
console.log('ResponseData' + JSON.stringify(responseData));
} catch (err) { }
};
fetchFoodgroups();
}, [sendRequest]);
const handleSelect = (event) => {
event.preventDefault();
console.log('Select.js: handleSelect- event e=' + event.target.value);
setSelValue(event.target.value);
}
return (
<React.Fragment>
<DropdownButton
className="form-control__select"
alignRight
title="Foodgroups"
id="dropdown-menu-align-right"
onSelect={handleSelect}
value={selValue}
>
<label htmlFor={props.id}>{props.label}</label>
{loadedFoodgroups.map(selectOptions => (
<Dropdown.Item
key={selectOptions.id}
className="form-control__select"
eventkey={selectOptions.id}>{selectOptions.name}
</Dropdown.Item>
))}
</DropdownButton>
</React.Fragment>
);
};
export default Select;
While the underlying issue is answered in some other questions (kind of), you have a few things you have to change, so here is a more specific answer for you:
First, the signature of onSelect is (eventKey, event) => ..., but in my testing the second param is not useful (target is null). So even if you switch to using the second param in your handler, it probably won't work. Instead, most people seem to be using the eventKey param like this:
const handleSelect = eventKey => {
event.preventDefault();
setSelValue(eventKey);
}
Which would probably meet your needs.
Before this will work for you, there are three typos to fix:
<Dropdown.Item
key={selectOptions._id}. <--you have ".id" i your code but the data is "._id"
className="form-control__select"
eventKey={selectOptions.name} <--you have "eventkey", with lowercase "k",
// should be upper case.
// You probably want the ".name" property here rather than
// "id", this is what will be passed into "onSelect"
>
So if you change your handler to use first param as eventKey and fix typos above, you should be able to set the state to the value selected from the dropdown button.

Resources