marionettejs / backbone.marionette

The Backbone Framework
https://marionettejs.com
Other
7.07k stars 1.26k forks source link

Avoiding duplicate instantiations of the same Behavior on a View #3647

Closed wuservices closed 5 years ago

wuservices commented 5 years ago

Description

When nesting or composing a reusable behavior, it's problematic that the behavior is initialized multiple times on the same view.

Copied from Gitter as suggested by @bazineta

Expected behavior

Ideally, since behaviors are bound to the view, they should only initialize once per view. This will allow behaviors to define nested behaviors as dependencies without them being initialized multiple times.

Actual behavior

Let's say we have InputView, which has a TrimOnBlurBehavior that trims text on blur and then displays a tooltip next to the input. The TrimOnBlurBehavior requires a nested TooltipBehavior to show the tooltip, but if InputView (or other behaviors) also require TooltipBehavior, the TooltipBehavior is initialized on InputView multiple times instead of just once.

As a result, we can't properly use nested behaviors without risking duplicate instantiations. Each behavior we use that utilizes a nested behavior can't require its own nested behavior dependencies and has a comment that says whatever view uses the behavior, also has to require the nested dependencies, which brittle and cumbersome.

Demo: https://jsfiddle.net/q475gnve/

You'll see here that since the reused behavior initializes on the view multiple times, its events are registered multiple times. The workaround for this is to only add behaviors on the main view, even if another behavior depends on a nested one.

Environment

  1. Marionette version: 3.x or 4-beta
  2. Backbone version: 1.3.3
wuservices commented 5 years ago

Looks like this is by design since you might be initializing the same behavior multiple times with different options.

There's no reason to assume a behavior should be unique. If one behavior includes another behavior that the view already has it may very well be reasonable that you want both behaviors to act. There can be two of the same behavior passed separate options to act on different elements even. If you want to insure that only one instance of a behavior is acting on a view, then have that behavior set a flag on the view and stop initializing if the flag is already set.

https://gitter.im/marionettejs/backbone.marionette?at=5c2dba0b33e089363b38af50