DEV Community

MasuqaT
MasuqaT

Posted on

Event handlers should be optional

Japanese Version | 日本語版

I realized that a component still works fine even if they do not receive event handlers. I explain why making event handlers optional makes sense and recommend doing it.

Component's event handlers to whom?

The event handlers of a component are defined for the caller, the parent component.

Thinking about the necessity of the events and event handlers in the component, they are used to realize the inverse data flow. Since "components should only update their own state", when it receives the data from its parent, indirectly expresses an update as an event, notifies it to its parent through the callback a.k.a. event handler, ask its parent to update the data. ref: Thinking in React - Step 5: Add Inverse Data Flow

Be aware that the event handlers, passed from the parent, do not affect directly on the (child) component. Of course, it looks as if it affects the (child) component when they are combined in the app, however, just the parent component changes itself instead of the (child) component. The parent component receives an event from the child through the event handlers, changes its state base on the event, then finally passes the updated data to the child. The parent component do not necessarily have to utilize the event and still be able to pass arbitrary data to the (child) component. Including whether to utilize the event or not, the update logic is fully under the parent's decision. In other words, the child component should not know about how the events and the event handlers are used in the parent component or others 1. Even if the event handler is undefined because of not passed from the parent, the child component does not change its self behavior.

Actually, all the event handlers for HTML elements are optional. We don't need to use focusin or input event and pass event handlers to all the events (except framework development). Moreover, HTML elements still work fine (raise no errors) even though we don't pass event handlers.

Since Vue and Angular use HTML based notation, event handlers are naturally regarded as optional. While in React, because it expresses event handlers as props like a value, event handlers tend to be marked as required. To avoid that, we need to call event handlers with optional chaining (?.) thoroughly, then add ? to the type if in TypeScript.

That said, isn't it necessary sometimes?

I believe everything will be still well even if we make all "event handlers" optional.

Function prop doesn't mean event handler

The type of the event handlers may be like (...args: [...Args]) => void, returns nothing. In some cases, the props includes normal functions which are not event handlers. For example, data fetching function (...args: [...Args]) => Promise<Data>2. These functions are required for the component to work fine. However, they are actually not event handlers and not related to this opinion, event handlers should be optional.

Component in cooperative group

As I mentioned that, even though the component is assumed to be work cooperatively in practice with the parent or others through the event handlers, receiving event handlers does not mean they are required for the single component (might be useless for others without passing event handlers). Passing event handlers is necessary to realize the behavior for a cooperative group, but as the parent-child relation of the component, as I mentioned above, it is just the problem how the cooperative group uses the event and not the problem of the single component as a primitive piece in the group.

Similarly in HTML elements, for example of <button type="button" />, (in normal use) it is not useful without use of click event, but that button does not raise an error. Because the parent component cannot achieve the requirement without click event or others, it pass event handlers to that button.

Inconvenience of required event handlers

Lastly, I list problems if event handlers are not optional.

Component catalog or test

In a component catalog, we explain many variations of the component. If event handlers are required, we have to pass made-up event handlers3 though they are not necessary in a catalog. Maybe, notifying the emit of the event is helpful only in the demo for behavior and not helpful for explaining color or size variants. (Remember that the long list of code for color and size variation of a basic button component with onClick={action("clicked")} in each item.)

The same is true for the test. If event handlers are required, we have to pass unnecessary event handlers for the test view point and the test purpose goes unclear. Also it increases boilerplate even in a test.

Conditionally unnecessary

Depending on the parameters, sometimes it is obvious that there are event handlers which are never called. For example, click event when <button /> is disabled. Aside from why4 there are permanently disabled control elements in SPA, it is a bad interface for a component user that has obviously unnecessary event handlers but necessary to pass something.

Previously, I put const noop = () => {}; to the required onChange prop of <input />-like component which is sometimes permanently disabled for the fixed value. I still regret doing that.


  1. As an interface which satisfies the requirement, e.g. the identifier names and parameters, the components need to "reach the consensus" with its generic(unspecified) parent. 

  2. I think most of the components should receive fetched data as a value prop, not through invoking a function prop. 

  3. Usually they are just a boilerplate such as Storybook Actions or console.log

  4. E.g. listing uneditable values along with normal editable values, brief promotion with button like "Coming Soon", etc. 

Discussion (0)