4

前言

在React的开发中,我们经常需要在 window 上注册一些事件, 比如按下 Esc 关闭弹窗, 按上下键选中列表内容等等。比较常见的操作是在组件 mount 的时候去 window 上监听一个事件, 在组件 unmount 的时候停止监听事件。下面给大家介绍几个骚操作。

WindowEventHandler

我们创建一个 WindowEventHandler 组件, 内容如下

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;
  }
}

现在比如我们想在组件A中监听 window 的 resize 事件,我们在 A 组件中可以这么写

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

这样我们在多个组件中就不需要每次都要写 mount 和 unmount 的钩子函数了,省了很多事情。

使用装饰器

我们可以给组件写一个统一的装饰器,和之前一样传入事件名和方法名就可以监听,等到组件卸载的时候就停止监听,代码如下

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;
        };
    };
}

类似这样的装饰器,同理我们想在 A 中监听 window 的 resize 事件,可以这么写

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

总结

这种小技巧提高了开发效率,少写了很多代码,可以在项目代码中尝试。


csywweb
232 声望8 粉丝

最担心的事不是写出ipcode,而是从不开始