reactjs에서 문서 키 누르기 듣기


82

escape눌러서 활성 반응 부트 스트랩 팝 오버를 닫기 위해 바인딩하고 싶습니다 . 여기에 코드가 있습니다.

_handleEscKey:function(event){
         console.log(event);
        if(event.keyCode == 27){
          this.state.activePopover.hide();
        }
   },

  componentWillMount:function(){
     BannerDataStore.addChangeListener(this._onchange);
     document.addEventListener("click", this._handleDocumentClick, false);
     document.addEventListener("keyPress", this._handleEscKey, false);
   },


   componentWillUnmount: function() {
     BannerDataStore.removeChangeListener(this._onchange);
      document.removeEventListener("click", this._handleDocumentClick, false);
      document.removeEventListener("keyPress", this._handleEscKey, false);
   },

하지만 아무 키나 눌러도 콘솔에 아무것도 기록되지 않습니다. 나는 창에서도 다른 경우를 들으려고 노력했지만 .'keypress ','keyup '등을했지만 내가 뭔가 잘못하고있는 것 같습니다.


이 모든 것을 훨씬 쉽게 만들어주는 React 용 keydown lib를 게시했습니다. github.com/jedverity/react-keydown
glortho

답변:


61

당신은 사용해야 keydown하지 keypress.

키 누르기 (더 이상 사용되지 않음)는 일반적으로 문서에 따라 문자 출력을 생성하는 키에만 사용됩니다.

키 누르기 (사용되지 않음)

키 누르기 이벤트는 키를 눌렀을 때 발생하며 해당 키는 일반적으로 문자 값을 생성합니다.

키 다운

keydown 이벤트는 키를 눌렀을 때 시작됩니다.


1
키 누르기는 더 이상 사용되지 않습니다.
TimeParadox

49

이것과 비슷한 문제가 있습니다. 수정 사항을 설명하기 위해 코드를 사용하겠습니다.

// for other devs who might not know keyCodes
var ESCAPE_KEY = 27;

_handleKeyDown = (event) => {
    switch( event.keyCode ) {
        case ESCAPE_KEY:
            this.state.activePopover.hide();
            break;
        default: 
            break;
    }
},

// componentWillMount deprecated in React 16.3
componentDidMount(){
    BannerDataStore.addChangeListener(this._onchange);
    document.addEventListener("click", this._handleDocumentClick, false);
    document.addEventListener("keydown", this._handleKeyDown);
},


componentWillUnmount() {
    BannerDataStore.removeChangeListener(this._onchange);
    document.removeEventListener("click", this._handleDocumentClick, false);
    document.removeEventListener("keydown", this._handleKeyDown);
},

작업을 수행하는 createClass 방식을 사용하고 있으므로 this정의 된 각 메소드에 내재 된 것처럼 특정 메소드에 바인딩 할 필요가 없습니다 .

여기 React 컴포넌트 생성의 createClass 메소드를 사용하는 jsfiddle이 작동 합니다.


9
매번 새 인스턴스를 제공하는 바인드로 인해 이벤트 리스너가 제대로 제거되지 않습니다. 바인드 반환 결과를 캐시하여 문서에서 적절하게 추가 및 제거하십시오
Steven10172

@ Steven10172 좋은 점은 생성자가 실제로 React.createClass 메서드에 정의되어 있지 않기 때문에 항상 getInitialState ()에서 바인딩 할 수 있습니다.
Chris Sullivan

위의 설명과 관련하여 이것은 이벤트 리스너를 바인딩하고 사용하는 좋은 예입니다. stackoverflow.com/questions/32553158/…
Craig Myles

1
componentWillMount의로 사용되지 않습니다 16.3 반응. IMO 대신에 이벤트 리스너를 componentDidMount.
Igor Akkerman

20

React Hooks를 사용할 수 있다면 좋은 방법은에 대한 useEffect것이므로 이벤트 리스너는 한 번만 구독되고 구성 요소가 마운트 해제 될 때 제대로 구독 해제됩니다.

아래 예제는 https://usehooks.com/useEventListener/ 에서 추출되었습니다 .

// Hook
function useEventListener(eventName, handler, element = window){
  // Create a ref that stores handler
  const savedHandler = useRef();

  // Update ref.current value if handler changes.
  // This allows our effect below to always get latest handler ...
  // ... without us needing to pass it in effect deps array ...
  // ... and potentially cause effect to re-run every render.
  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(
    () => {
      // Make sure element supports addEventListener
      // On 
      const isSupported = element && element.addEventListener;
      if (!isSupported) return;

      // Create event listener that calls handler function stored in ref
      const eventListener = event => savedHandler.current(event);

      // Add event listener
      element.addEventListener(eventName, eventListener);

      // Remove event listener on cleanup
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    },
    [eventName, element] // Re-run if eventName or element changes
  );
};

또한, 예를 들면, NPM에서 설치할 수 있습니다 npm i @use-it/event-listener- - 여기에 프로젝트 참조 https://github.com/donavon/use-event-listener을 .

그런 다음 구성 요소에서 사용하려면 이벤트 이름과 핸들러를 전달하는 기능 구성 요소 내에서 호출하면됩니다. 예를 들어 console.logEscape 키를 누를 때마다 원하는 경우 :

import useEventListener from '@use-it/event-listener'

const ESCAPE_KEYS = ['27', 'Escape'];

const App = () => {
  function handler({ key }) {
    if (ESCAPE_KEYS.includes(String(key))) {
      console.log('Escape key pressed!');
    }
  }

  useEventListener('keydown', handler);

  return <span>hello world</span>;
}

앱이 기능적 구성 요소가 아닌 경우 사용할 수 없음
ashubuntu

이것을 게시 해 주셔서 감사합니다. 글로벌 키보드 핸들러에서 엄청난 메모리 누수 문제를 해결하는 데 도움이되었습니다. FWIW, "save listeners to a ref"효과가 정말 중요합니다. 이벤트 핸들러 useEffect를 추가 하는 종속성 배열 에 전달하지 마십시오 document.body.onKeyDown.
aendrew

@aendrew : 처리기를 ref에 저장하고 함수를 선언하는 것과 다른 점은 무엇입니까?
thelonglqd

@thelonglqd 그렇지 않으면 이벤트 핸들러로 여러 번 추가되기 때문이라고 생각합니다.하지만 그것에 대해 인용하지 마십시오.
aendrew

2

이 질문과 더 관련이있는 Jt oso의 답변 버전입니다. 외부 라이브러리 또는 API 후크를 사용하여 리스너를 바인딩 / 바인딩 해제하는 다른 답변보다 훨씬 간단하다고 생각합니다.

var KEY_ESCAPE = 27;
...
    function handleKeyDown(event) {
        if (event.keyCode === KEY_ESCAPE) {
            /* do your action here */
        }
    }
...
    <div onKeyDown={handleKeyDown}>
...

3
먼저 항목에 초점을 맞춰야합니다. 글로벌 이벤트 리스너를 원할 경우 처음에는 body 요소가 포커스되어 있기 때문에 트리거되지 않을 수 있습니다.
n1ru4l

1

탭 가능한 div에 대해 동일한 요구 사항이있었습니다.

나를 위해 다음 코드는 items.map ((item) => ...

  <div
    tabindex="0"
    onClick={()=> update(item.id)}
    onKeyDown={()=> update(item.id)}
   >
      {renderItem(item)}
  </div>

이것은 나를 위해 일했습니다!


1

글로벌 이벤트 리스너를 갖고 싶었고 React Portal 사용으로 인해 이상한 동작이있었습니다. 문서 내의 포털 모달 구성 요소에서 취소 되었음에도 불구하고 문서 요소에서 이벤트가 계속 트리거되었습니다.

전체 구성 요소 트리를 래핑하는 루트 개체에서만 이벤트 리스너를 사용하는쪽으로 이동했습니다. 여기서 문제는 처음에는 본체가 루트 요소가 아니라 초점이 맞춰져 있으므로 트리 내의 요소에 초점을 맞추면 이벤트가 먼저 시작된다는 것입니다.

내가 찾은 해결책은 tabindex를 추가하고 효과 후크로 자동으로 초점을 맞추는 것입니다.

import React from "react";

export default GlobalEventContainer = ({ children, ...props }) => {
  const rootRef = React.useRef(null);
  useEffect(() => {
    if (document.activeElement === document.body && rootContainer.current) 
      rootContainer.current.focus();
    }
  });

  return <div {...props} tabIndex="0" ref={rootRef}>{children}</div>
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.