0

I've got a class that looks like this and I'm trying to trigger certain function when the enter key is pressed.

class List extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            ...
        };
        this.setReady = this.setReady.bind(this);
    }

    componentDidMount() {
        //other stuff
        document.addEventListener('keydown', this.onKeyPressed.bind(this));
    }

    componentWillUnmount() {
        document.removeEventListener('keydown', this.onKeyPressed.bind(this));
    }

    handleSearch() {
         let value = document.getElementById('search-holder').value;
         let names = ['address', 'page'];
         let values = [value, 0];
         this.updatePublications(names, values, true);
    }

    onKeyPressed(e) {
        if (e.keyCode === 13) {
            this.handleSearch();
        } 
    }

    render(){...}

}

Basically I wanted to trigger handleSearch() when enter is pressed inside that page. However handleSearch() seems to be triggered when enter key is pressed on the whole webapp. How can I fix this?

List is called only on my App.js

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            username: null,
        };
    }

    componentDidMount() {
        this.setState({username: LocalStorageService.getUsername()});
    }

    updateUsername = (username) => {
        this.setState({username: username});
    };

    render() {
        return (
            <div className='App'>
                <BrowserRouter basename={process.env.PUBLIC_URL}>
                    <Suspense fallback={<div>Loading</div>}>
                        <Navbar updateUsername={this.updateUsername} username={this.state.username} />
                        <Switch>
                            <Route exact path='/' component={Home} />
                            <Route exact path='/List' component={List} />
                            // all my other routes
                        </Switch>
                    </Suspense>
                </BrowserRouter>
            </div>
        );
    }
}

export default App;
Flama
  • 772
  • 3
  • 13
  • 39

3 Answers3

2

When you use document.addEventListener('keydown', this.onKeyPressed.bind(this));, you say that the event listener should be added to the whole document.

Use element reference when you add the lister.

<YOUR-ELEMENT>.addEventListener('keydown', this.onKeyPressed.bind(this));

Similar way, when you remove the event listener on unmount, remove it from the element itself.

To read more on element reference, you could see the official documentation here

codePG
  • 1,754
  • 1
  • 12
  • 19
  • What would be `` in this case? My input html id? – Flama Feb 10 '20 at 04:21
  • That is an html element (input element in this case), which could be simply referred also be `document.getElementById("inputElementId");` – codePG Feb 10 '20 at 04:23
  • See for example here (https://stackoverflow.com/questions/36180414/reactjs-add-custom-event-listener-to-component) – codePG Feb 10 '20 at 04:25
  • I get `this.handleSearch is not a function` when I do that. It does work with an alert though but it would be great if I get to call that function – Flama Feb 10 '20 at 04:26
  • You would need to bind you handle search in the constructor I guess, `this.handleSearch = this.handleSearch.bind(this);` – codePG Feb 10 '20 at 04:27
  • What you could try further is to bind onKeyPressed also. The issue mostly is that the handleSearch is not in the scope, if the above did not work, try converting onKeyPressed and handleSearch as arrow functions. – codePG Feb 10 '20 at 04:41
  • 1
    I just converted onKeyPressed to an arrow function and it seems to work. Thank you very much. – Flama Feb 10 '20 at 04:44
0

If you're working on a single-page application, the document persists, so your event listener doesn't go away. You attempted to remove it with the lifecycle function componentWillUnmount() but if the List component was re-rendered because of a state update the instance of the function in the original event listener doesn't exist anymore, so you can't remove it.

Try putting the functions in the parent component and passing them down through props so that the functions persist and can be used later to remove the event listener.

Sydney Y
  • 2,912
  • 3
  • 9
  • 15
0

You're adding a listener in your App component which I am guessing renders almost everything else in your app. So even though you might be somewhere else, if that component is rendered by App component then the listener will still be active. I suggest you use the onKeyPress attribute of you html input element where the search is.

Rough example:

handleOnKeyPress = (event) => {
  // on key press logic here 
}

render() {
  return (
    <>
      {/* ... */}
        <input type="search" onKeyPress={handleOnKeyPress} />
      {/* ... */}
    </>
  )
}


If you want the logic to stay in a parent component just define it there and pass it down as props.

Hope this is the answer you're looking for.

Mario Subotic
  • 131
  • 1
  • 5