Listeners Without Renders in React

January 25, 2018
react javascript

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

React State With Class Properties

February 2, 2018
react javascript

React Waypoint Revealable

November 8, 2017
react emotion

Do Expressions and Optional Chaining

September 1, 2017
javascript babel