Warning: Under Construction. I am using this site as my playground as I buildĀ gatsby-mdx and gatsby themes. During this time I will also repost new contentĀ to Medium

Listeners Without Renders in React

Sometimes you run into a situation where you want to have some logic that isn't associated with any rendered output but starts/stops when a certain page is rendered. React can handle this by rendering null and using lifecycle events to deal with listener attachment and cleanup.

The following code creates two components, one representing the App and one to handle the logic. App mounts or unmounts the KeydownListener once every second, forcing componentDidMount and componentWillUnmount to fire.

import React, { Component } from "react";
import { render } from "react-dom";
class KeydownListener extends Component {
state = {
id: Math.random().toFixed(4)
};
componentDidMount() {
window.document.addEventListener(
"keydown",
this.handleKeyboardNavigation,
false
);
}
componentWillUnmount() {
window.document.removeEventListener(
"keydown",
this.handleKeyboardNavigation,
false
);
}
handleKeyboardNavigation = e => {
console.log(`${this.state.id}: caught key event`);
};
render() {
console.log(this.state.id);
return null;
}
}
class App extends Component {
state = {
show: false
};
componentDidMount() {
this.interval = setInterval(() => {
this.setState(state => ({ ...state, show: !state.show }));
}, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return <div>{this.state.show && <KeydownListener />}</div>;
}
}
render(<App />, document.getElementById("root"));

The output for the above is the following (which you can test in codesandbox.

0.0705
0.3664
0.6017
0.1500
0.8428
0.2668
0.2668: caught key event
0.2668: caught key event
0.2668: caught key event
0.2328
0.8442
0.7064
0.8180