loading...
Cover image for Reusable DOM behavior in React vs Ember

Reusable DOM behavior in React vs Ember

_raja_sk_ profile image Raja SK Updated on ・4 min read

As a developer, while writing some common logic of code, our mind would have thought of the following question.

"How we are gonna share these code between several components". 🤔

In case, if the logic of the code handles DOM behavior(i.e., the block of code which triggers when an element is added, updated or removed from the DOM tree), the answer is pretty simple - reusable DOM behavior.

Each framework handles it differently. Let's just dig into React and Ember and see how they handle it.

How does React achieve it?

React's class-based components handles DOM behavior through the following lifecycle hooks:

  • componentDidMount - fires when the component is inserted to the DOM
  • componentDidUpdate - fires when the component is being re-rendered.
  • componentWillUnmount - fires just before a component is being removed from the DOM.

React's functional components manages to do this with a single hook - useEffect.

useEffect() => {
  // logic when component is rendered and re-rendered...
  // replaces componentDidMount and componentDidUpdate

  return () => {
    // teardown logic...
    // replaces componentWillUnmount
  }
}

We can reuse this useEffect hook by creating a custom hook and importing it wherever we want.

How does Ember fills the gap?

In classic ember, generally, a mixin is a group of logic that can be reused in multiple places.DOM behaviors can also be reused through component mixins with the help of component lifecycle hooks.

We can write a mixin named tabs.js like this

import Mixin from '@ember/object/mixin';

export default Mixin.create({
  didInsertElement() {
    this._super();
    activateTabs(this.element);
  }

  willDestroyElement() {
    this._super();
    deactivateTabs(this.element);
  }
})

and consume the Tabs mixin like this

import Component from '@ember/component';

export default Component.extend(Tabs, {
  // ...
});

Now, Ember Octane comes with a better solution - ember-modifiers.

Ember modifiers are just like the helpers in ember but the difference is that the former applies directly to an element and the latter is passed as an attribute to an element.

We can access the modifiers using the double curly-braces {{}} syntax.

Ember modifiers can be implemented in two ways.

  1. Ember functional modifier
  2. Ember class-based modifier

As in every other framework, functional implementation is way simpler than class-based implementation, but the latter provides more control.

Syntax for ember functional modifiers - overall same as the useEffect in react.

export default modifierName((element, positional, named) => {
  // do side effects...
  return () => {
    //teardown logic
  }
}

Syntax for ember class-based modifiers - same as the react class-based component hooks.

export default class ModifierName extends Modifier {
  didReceiveArguments() {
    // do side effects...
  }
  willRemove() {
    // teardown logic
  }
} 

If we have a thought that ember modifiers are new to ember, then we have probably missed something while working with ember. We may come across two syntaxes for invoking action in templates.
This one demonstrates an action helper.

<button onclick={{action "handleClick"}}> Click Me! </button>

And this demonstrates an action modifier (one of the inbuilt ember modifiers).

<button {{action "handleclick"}}> Click Me! </button>

Oh! I've worked with a modifier without even knowing it's a modifier. 😅

Let's say, we need an autofocus modifier which when applied to an element, should focus the element on every render.

File: app/modifiers/autofocus.js - functional implementation

export default function autofocus((element) => {
  element.focus();
}

We can easily apply this modifier to as many elements as we want.

File: app/components/my-component.hbs

<input value=this.value {{autofocus}} />

This approach to autofocus has a huge advantage over the HTML input element's autofocus attribute. This autofocus modifier gets triggered for each render and re-render of that element. But the autofocus attribute gets triggered only on the first render.

Advantages of ember modifiers over ember mixins

1. Modifiers allow targeting specific elements easily.

  • In case of mixins, if we need to apply the mixin to an element inside a component, then we have to make that element as a separate component and extend the js class with that mixin. This will create chaos.
  • Ember modifiers can be applied to any element we want without making it a separate component.

2. Modifiers work with template only components.

  • Without a js file, the mixins are useless. They have no place to be extended.
  • Modifiers are invoked only in the templates. There is no need for a js file.

3. Modifiers work with tag-less components and Glimmer components.

Nowadays, Tag-less components have lifecycle hooks, but they don't have this.element in it. Glimmer components also don't have a wrapping element and hence no this.element.

  • With mixins, it is hard to do the DOM manipulations.
  • With modifiers, we can simply have the logic in a modifier file separately and invoke it in the template file alone.

Which one wins? React's useEffect() hook or ember modifiers?

Both do the same jobs in their respective environments and it's hard to compare them. But, ember modifiers allow us to grab the element directly through arguments. This can't be done in useEffect() hook without the help of other hooks like useRef().

Through this point, ember proves one more time that it is more of an organized framework, which handles most of the stuff internally and makes the end-developer care more on other kinds of stuff.


Up next ⚠️

Curious about how ember modifiers get to be managed internally? Go through my next blog ! 👋

Posted on by:

_raja_sk_ profile

Raja SK

@_raja_sk_

A front-end developer, works with Ember and React. Keen on designing as well.

Discussion

markdown guide