React将所有事件传递给子组件

3

在React中,是否可以将所有事件传递给子元素?

例如,我有一个自定义的Button类,它(简化后)看起来像这样:

class Button extends Component {
  
  constructor (props) {
    super(props);

    this.onClick      = this.onClick.bind(this);
  }
  
  /* .... */
  
  onClick (ev) {
    const { disabled, onClick } = this.props;
    if (!disabled) {
      onClick(ev);
    }
  }
  
  render () {
     const {
      children,
      disabled,
      type
     } = this.props;
    
    return (
      <button
        disabled={disabled}
        onClick={this.onClick}
        ref="button"
        type={type}
      >{children}</button>
  }
  
  
}

我不知道以后可能会使用哪些事件(例如onMouseDown,onMouseUp,onBlur,onKeyDown,onTouchStart等)

是否有可能将所有可能的事件传递给按钮元素而无需为每个可能的事件编写一个prop属性?

将 {...this.props} 添加到按钮元素中不是我想要的,因为它会传递所有的 prop 属性,而某些属性(例如在此示例中省略的 className)不应直接传递。

我想过克隆 props 对象并删除不应直接传递的 props,但这感觉像是一个 hack。有人知道更简洁的方法吗?


2
给你的事件命名空间? {...this.props.events} - Rob M.
1
感谢您的评论@RobM。我考虑了您的建议,但我宁愿尽可能接近默认的React Api。目前,我编写了一个过滤以“on”开头的属性的函数。这是我迄今为止最接近的方法。我将把该函数发布为答案,供有兴趣的人参考。 - Barry127
2个回答

2
我已经编写了一个函数,用于遍历props并过滤掉所有以“on”开头的属性。这是我目前为止最接近的解决方案。如果有帮助的话,请参考以下代码:

/* helpers.js */

export function filterEvents (props, ignore = []) {
  let events = {};
  for (let property in props) {
    if (props.hasOwnProperty(property)) {
      if (property.startsWith('on') && ignore.indexOf(property) === -1) {
        events[property] = props[property];
      }
    }
  }

  return events;
}

/* Tests for the filterEvents */
  
import { expect } from 'chai';

import { filterEvents } from './helpers';

describe('filterEvents', () => {

  const props = {
    className: 'someClass',
    disabled: true,
    onBlur: 'onBlur',
    onClick: 'onClick',
    onMouseDown: 'onMouseDown',
    onMouseUp: 'onMouseUp'
  };

  it('only returns keys starting with on', () => {
    const expected = {
      onBlur: 'onBlur',
      onClick: 'onClick',
      onMouseDown: 'onMouseDown',
      onMouseUp: 'onMouseUp'
    };

    expect(filterEvents(props)).to.deep.equal(expected);
  });

  it('only returns keys starting with on minus the ones in the ignore array', () => {
    const expected = {
      onBlur: 'onBlur',
      onMouseUp: 'onMouseUp'
    };

    const ignore = ['onClick', 'onMouseDown'];
    expect(filterEvents(props, ignore)).to.deep.equal(expected);
  });

});


/* Using the function inside a component */

import { filterEvents } from './helpers'; //at the top of the components file

//Inside the render method:

const events = filterEvents(this.props, ['onClick']); //don't include onClick it's handled like the questions example

return (
  <button
    disabled={this.props.disabled}
    onClick={this.onClick}
    {...events}
  >
    {this.props.children}
  </button>
);


0

我已经采用了Barry127的答案,并将React中的所有event handlers添加到了一个对象中。

const acceptedEventHandlersForComponentValidationFunction = {
    clipBoard: [
        "onCopy",
        "onCut",
        "onPaste",
        "onCopyCapture",
        "onCutCapture",
        "onPasteCapture"
    ],
    composition: [
        "onCompositionEnd",
        "onCompositionStart",
        "onCompositionUpdate",
        "onCompositionEndCapture",
        "onCompositionStartCapture",
        "onCompositionUpdateCapture"
    ],
    keyboard: [
        "onKeyDown",
        "onKeyPress",
        "onKeyUp",
        "onKeyDownCapture",
        "onKeyPressCapture",
        "onKeyUpCapture"
    ],
    focus: ["onFocus", "onBlur", "onFocusCapture", "onBlurCapture"],
    form: [
        "onChange",
        "onInput",
        "onInvalid",
        "onReset",
        "onSubmit",
        "onChangeCapture",
        "onInputCapture",
        "onInvalidCapture",
        "onResetCapture",
        "onSubmitCapture"
    ],
    generic: ["onError", "onLoad", "onErrorCapture", "onLoadCapture"],
    mouse: [
        "onClick",
        "onContextMenu",
        "onDoubleClick",
        "onDrag",
        "onDragEnd",
        "onDragEnter",
        "onDragExit",
        "onDragLeave",
        "onDragOver",
        "onDragStart",
        "onDrop",
        "onMouseDown",
        "onMouseEnter",
        "onMouseLeave",
        "onMouseMove",
        "onMouseOut",
        "onMouseOver",
        "onMouseUp",
        "onClickCapture",
        "onContextMenuCapture",
        "onDoubleClickCapture",
        "onDragCapture",
        "onDragEndCapture",
        "onDragEnterCapture",
        "onDragExitCapture",
        "onDragLeaveCapture",
        "onDragOverCapture",
        "onDragStartCapture",
        "onDropCapture",
        "onMouseDownCapture",
        "onMouseMoveCapture",
        "onMouseOutCapture",
        "onMouseOverCapture",
        "onMouseUpCapture"
    ],
    pointer: [
        "onPointerDown",
        "onPointerMove",
        "onPointerUp",
        "onPointerCancel",
        "onGotPointerCapture",
        "onLostPointerCapture",
        "onPointerEnter",
        "onPointerLeave",
        "onPointerOver",
        "onPointerOut",
        "onPointerDownCapture",
        "onPointerMoveCapture",
        "onPointerUpCapture",
        "onPointerCancelCapture",
        "onGotPointerCaptureCapture",
        "onLostPointerCaptureCapture",
        "onPointerOverCapture",
        "onPointerOutCapture"
    ],
    selection: ["onSelect", "onSelectCapture"],
    touch: [
        "onTouchCancel",
        "onTouchEnd",
        "onTouchMove",
        "onTouchStart",
        "onTouchCancelCapture",
        "onTouchEndCapture",
        "onTouchMoveCapture",
        "onTouchStartCapture"
    ],
    ui: ["onScroll", "onScrollCapture"],
    wheel: ["onWheel", "onWheelCapture"],
    media: [
        "onAbort",
        "onCanPlay",
        "onCanPlayThrough",
        "onDurationChange",
        "onEmptied",
        "onEncrypted",
        "onEnded",
        "onError",
        "onLoadedData",
        "onLoadedMetadata",
        "onLoadStart",
        "onPause",
        "onPlay",
        "onPlaying",
        "onProgress",
        "onRateChange",
        "onSeeked",
        "onSeeking",
        "onStalled",
        "onSuspend",
        "onTimeUpdate",
        "onVolumeChange",
        "onWaiting",
        "onAbortCapture",
        "onCanPlayCapture",
        "onCanPlayThroughCapture",
        "onDurationChangeCapture",
        "onEmptiedCapture",
        "onEncryptedCapture",
        "onEndedCapture",
        "onErrorCapture",
        "onLoadedDataCapture",
        "onLoadedMetadataCapture",
        "onLoadStartCapture",
        "onPauseCapture",
        "onPlayCapture",
        "onPlayingCapture",
        "onProgressCapture",
        "onRateChangeCapture",
        "onSeekedCapture",
        "onSeekingCapture",
        "onStalledCapture",
        "onSuspendCapture",
        "onTimeUpdateCapture",
        "onVolumeChangeCapture",
        "onWaitingCapture"
    ],
    image: ["onLoad", "onError", "onLoadCapture", "onErrorCapture"],
    animation: [
        "onAnimationStart",
        "onAnimationEnd",
        "onAnimationIteration",
        "onAnimationStartCapture",
        "onAnimationEndCapture",
        "onAnimationIterationCapture"
    ],
    transition: ["onTransitionEnd", "onTransitionEndCapture"],
    other: ["onToggle", "onToggleCapture"]
}

/*
- Component props event handler vilidation
Return all valid events to be used on a component

{
    acceptedEventHandlerTypes: [
        "${event handler type}"
    ],
    eventHandlers: {
        ${event}: "${callback function}" // ${event} can contain "Capture" at the end to register the event handler for the capture phase
    }
}
*/
const validateComponentPropsEventHandlers = (
  acceptedEventHandlerTypes,
  eventHandlers = {}
) => {
  if (Object.keys(eventHandlers).length == 0) {
    return {}
  }

  // Fill eventsForSpecifiedType with only the required events
  let eventsForSpecifiedType = {}
  let eventsCount = 0

  for (const eventHandlerType in acceptedEventHandlerTypes) {
    if (
      acceptedEventHandlerTypes[eventHandlerType] in
      acceptedEventHandlersForComponentValidationFunction
    ) {
      const newEvents =
        acceptedEventHandlersForComponentValidationFunction[
          acceptedEventHandlerTypes[eventHandlerType]
        ]

      eventsForSpecifiedType[
        acceptedEventHandlerTypes[eventHandlerType]
      ] = newEvents

      eventsCount += newEvents.length
    }
  }

  // Fill events
  let events = {}
  let eventsCountCheck = 0

  const checkIfEventsCountHasBeenReached = () =>
    eventsCountCheck == eventsCount

  for (const eventHandler in eventHandlers) {
    if (checkIfEventsCountHasBeenReached()) {
      return events
    }

    // Append event handler to events object if it is recognised
    Object.values(eventsForSpecifiedType).forEach(
      (EVENT_HANDLERS) => {
        if (
          EVENT_HANDLERS.includes(eventHandler) &&
          !(eventHandler in events)
        ) {
          events[eventHandler] = eventHandlers[eventHandler]

          eventsCountCheck += 1
        }

        if (checkIfEventsCountHasBeenReached()) {
          return events
        }
      }
    )
  }

  return events
}

// Usage
const test = () => {console.log("test")}
const events = validateComponentPropsEventHandlers(["mouse"], { onClick: test })
console.log(events)
// <button {...events}>Button</button>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接