Gracefully register events in the React component

Preface

In the development of React, we often need to register some events on the window, such as pressing Esc to close the pop-up window, pressing up and down keys to select the list content, etc. A common operation is to listen to an event on the window when the component is mounted and stop listening to the event when the component is unmount ed. Next, I'd like to introduce a few operations.

WindowEventHandler

We create a WindowEventHandler component as follows

import PropTypes from 'prop-types';
import { Component, PureComponent } from 'react';

export default class WindowEventHandler extends (PureComponent || Component) {
  static propTypes = {
    eventName: PropTypes.string.isRequired,
    callback: PropTypes.func.isRequired,
    useCapture: PropTypes.bool,
  };

  static defaultProps = {
    useCapture: false,
  };

  componentDidMount() {
    const { eventName, callback, useCapture } = this.props;
    window.addEventListener(eventName, callback, useCapture);
  }

  componentWillUnmount() {
    const { eventName, callback, useCapture } = this.props;
    window.removeEventListener(eventName, callback, useCapture);
  }

  render() {
    return null;
  }
}

Now, for example, if we want to listen to the resize event of window in component A, we can write this in component A

export default class A extends (PureComponent || Component) {
  
  handleResize = () => {
   // dosomething...
  }
  render() {
    return (
        <div>
            //I am component A 
            <WindowEventHandler eventName="resize" callback={this.handleResize} />
        </div>
    );
  }
}

In this way, we don't need to write hook functions of mount and unmount in multiple components, which saves a lot of things.

Using decorators

We can write a unified decorator for the component. As before, we can pass in the event name and method name to listen, and stop listening when the component is uninstalled. The code is as follows

export default function windowEventDecorator(eventName, fn) {
    return function decorator(Component) {
        return (...args) => {
            const inst = new Component(...args);
            const instComponentDidMount = inst.componentDidMount ? inst.componentDidMount.bind(inst) : undefined;
            const instComponentWillUnmount = inst.instComponentWillUnmount ? inst.componentWillUnmount.bind(inst) : undefined;

            const callback = (e) => { 
               typeof inst[fn] === 'function' && inst[fn](); 
            };
            inst.componentDidMount = () => {
                instComponentDidMount && instComponentDidMount();
                document.body.addEventListener(eventName, callback, true);
            };
            inst.componentWillUnmount = () => {
                instComponentWillUnmount && instComponentWillUnmount();
                document.body.removeEventListener(eventName, callback, true);
            };
            return inst;
        };
    };
}

Similar to such A decorator, similarly, we want to listen to the resize event of window in A, which can be written as follows

@windowEventDecorator('resize', 'handleResize');
export default class A extends (PureComponent || Component) {
  
  handleResize = () => {
   // dosomething...
  }
  render() {
    return (
        <div>
            //I am component A 
        </div>
    );
  }
}

summary

This kind of small skill improves the development efficiency, writes a lot of code less, may try in the project code.

Tags: Javascript React less

Posted on Mon, 02 Dec 2019 05:43:57 -0500 by kavisiegel