2

After doing some tutorials online, I stumbled upon some weird JavasSript syntax that I'm not familiar with. In this tutorial, the author does this

export default ComposedComponent => class extends 

Can someone explain to me what it means? It looks like a fat arrow function but there doesn't seem to be any arguments. it is also multilined but there are no brackets. What does this exactly mean?

2 Answers 2

2

Introduction:

We "traditionally" declare ES6 classes like so:

export default class Apple extends Fruit { ... }

Where Apple is just a name for the class. In your example:

export default ComposedComponent => class extends Component { ... }

Is just an arrow function, with an argument ComposedComponent, and an anonymous class. It's basically equivalent to:

export default (ComposedComponent) => {
    return class extends Component {
        ...
    }
}

Where class extends Component is an unnamed class which extends React's Component.

What it is:

What this is is a higher order component wrapper. You can think of it as a React component that wraps another one - a component factory. It follows the syntax:

HigherOrderComponentFactory = WrappedComponent: React.Component => NewComponent: React.Component

Where WrappedComponent is the component being wrapped and NewComponent being the new, higher order component which is returned - both inheriting from React.Component. Now if you take a look at the render method:

return (
  <ComposedComponent
    {...this.props}
    subscribe={this.subscribe.bind(this)}
    subscriptionReady={this.subscriptionReady.bind(this)}
  />
);

Here, <ComposedComponent {...this.props} /> is equivalent to:

React.createElement(ComposedComponent, this.props, null);

Application:

Let's take another look at the factory declaration:

export default ComposedComponent => class extends Component { ... }

An arrow function (which is comparable to the factory mentioned above) is exported as default, and has an argument ComposedComponent. Here, ComposedComponent is the component that will be wrapped by the factory, and class extends Component { ... } will be the new, higher order component.

In your tutorial, the arrow function (or HOC factory) is called like so:

SubscribeComponent(App)

App is the component to be wrapped, in other words ComposedComponent. The returned component is the higher order component.

More reading about higher order components and their advantages here.

Sign up to request clarification or add additional context in comments.

Comments

1

It is an arrow function, and it does have an argument (ComposedComponent). It is also "multi-lined", but it's a single statement, ergo it does not need brackets.

export default ComposedComponent => class extends
Component {
  // ...
}

is every bit the same as

export default ComposedComponent => class extends Component { ... }

And the class which is returned is anonymous class X extends Component.


edit

to expand upon the comments below:

There are three things going on, on that one line, which might cause confusion.

export default (ComposedComponent) => {
  return class UnnamedClass extends Component {
    render () {
      return <ComposedComponent />;
    }
  };
}

written in this fashion it should be easier to see what's going on.

  1. export statement, exporting a function

  2. an arrow function with one argument and a body which is just a return statement (whether the statement is split across lines or not)

  3. an anonymous class is constructed

As you know, arrow functions don't need parentheses when they have one argument, nor do they need braces when they only have a return statement.

x => x;

That's true, even if the return statement spans multiple lines which can't have semicolon insertion.

() => "1" +
      "2" +
      "3";

Which is where

() => class extends
Component {
  // ...
}

comes in...

That's not how I'd write it, but there it is. I'd probably prefer to do:

() => class extends Component {
  // ...
}

to match the JS we're all used to (though decorators and TS may change that).

The last part is important. Classes are happily anonymous, unless you put them in a position where they need a name.
And if you think about it, that makes complete sense. They're really just cleaned-up versions of constructor functions from yesteryear, and have nothing to do with classes in most other languages.

const X = class { constructor () { } };
const x = new X();

is no different than saying

const X = function () { };
const x = new X();

so

class X {
  constructor () { }
}

and

function X () { }

are equivalent.

Anywhere you could pass a function, with the expectation that somebody will new it, you could pass an anonymous class.

makeInstance(class {
  a () { }
  b () { }
});

Tie that all together, and you get

export default ComposedComponent => class extends
Component {
  render () {
    return (
      <ComposedComponent >
       // ...
      </ComposedComponent>
    );
  }
}

4 Comments

@AndrewLi it's not in the place of... It's an anonymous class. class ___ extends Component. You're looking at (ComposedComponent) => class UNNAMED_CLASS extends Component { /* ... */ }
does this mean that ComposedComponent is actually the argument for the UNAMED_CLASS?
@LawrenceVo No, you're thinking too hard about it. ComposedComponent is only used in the render() method. Otherwise, the class being built is an anonymous class. var anonymous = class extends Component {}; export default class extends Component {} export default function (ComposedComponent) { return class extends Component { render () { return <ComposedComponent />; } }; }
@LawrenceVo I see where it's very easy to think this is a special case of ES6 syntax. It's nothing more than three things that are bunched together and seem trickier than they are. 1) you are declaring an export 2) that export is an arrow-function with one argument (ComposedComponent) and one return statement class extends Component { }, and thus needs neither parentheses nor braces 3) classes can be 100% anonymous, just like functions. Tie all three together, and you have a tricky time parsing what's going on. I know I had to take a second look.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.