如何通过事件名称将事件处理程序添加到 JSX 节点?

How to add an event handlers to JSX node by event name?

我有一种常见的情况,React 组件接收一个事件名称和一个处理函数作为道具,我必须将这个函数附加到该组件返回的元素中的所述事件的事件处理程序:

const Component(eventName, handler) => {
   return <div>{...}</div>
}

我最初的想法是创建一个道具对象并散布在组件上:

const Component(eventName, handler) => {

   const props = { ['on'+eventName]: handler };
   
   return <div {...props}>
       {...}
   </div>
}

问题是事件名称是小写的,例如clickdragend

而 React 期望使用驼峰式语法。

所以如果我收到 'click' 字符串作为 prop,它必须变成 props 对象上的 onClick 键。这不像将事件大写并在其前面加上 'on' 那样简单,因为有些事件有多个单词。例如,dragend 需要变成 onDragEnd 才能让 React 满意。

有没有人建议我如何在不维护大量事件名称列表的情况下完成此操作 -> 映射到此组件中的 jsx 属性?

事件处理函数是否期望在它们被调用时,它们将被传递给浏览器事件对象或 React 合成事件对象?

如果他们期待浏览器事件(这似乎很可能,因为他们使用的是浏览器事件命名约定),那么注册所有具有效果的事件可能是有意义的 - 见下文。

请注意,我已经完成了记忆事件道具对象的工作。这对于功能而言并不是绝对必要的,但如果此事件对象在您的组件的父级中频繁更改身份,它将有助于提高性能:

import React, { useEffect, useRef } from "react";
import isDeepEqual from "fast-deep-equal/react";

const testEvents = {
  click: () => console.log("click event handler fired!"),
  mouseover: () => console.log("mouseover event handler fired!"),
  mouseleave: () => console.log("mouseleave event handler fired!")
};

const TestComponent = ({ events }) => {
  const ref = useRef(null);
  const memoizedEvents = useMemoizedEvents(events);
  useEffect(() => {
    const currentRef = ref.current;
    Object.entries(memoizedEvents).forEach(([name, handler]) =>
      currentRef.addEventListener(name, handler)
    );
    // This function cleans up old event handlers.
    // It will run whenever memoziedEvents changes before the new handlers are registered.
    return () =>
      Object.entries(memoizedEvents).forEach(([name, handler]) =>
        currentRef.removeEventListener(name, handler)
      );
  }, [memoizedEvents]);
  return (
    <div ref={ref}>
      DOM Element with Event Handlers
    </div>
  );
};

/**
 * This hook makes sure that the "events" object only changes identity if any of the things within it have changed.
 * This way, the effect that registers/unregisters the click handlers won't run unnecessarily.
 */
function useMemoizedEvents(events) {
  const eventsRef = useRef();
  if (!isDeepEqual(eventsRef.current, events)) {
    eventsRef.current = events;
  }
  return eventsRef.current;
}

const App = () => <TestComponent events={testEvents} />;

this CodeSandbox