3

Say I have a simple React stateless component like so:

const myComponent = () => {
    const doStuff = () => {
        let number = 4;

        return doubleNumber(number);
    };

    const doubleNumber = number => {
        return number * 2;
    };

    return <div>Hello {doStuff()}</div>;
};

export default myComponent;

Based on the eslint error I receive, and my understanding of how 'const' works, I assumed that this component would not render, since the function 'doubleNumber()' is used by the function 'doStuff()' before it is initialized. However, whenever I use this component, it renders as expected - why doesn't it throw an exception? Does this mean the order of 'const' variables in React components can be whatever we like?

HJordan35
  • 160
  • 2
  • 9
  • 2
    The method `doStuff` is intialized before `doubleNumber` is intialized but it is called ( executed ) after `doubleNumber` is intialized. – iamkhush Sep 18 '19 at 21:25
  • 2
    They are all in scope at the time the function is called, so I don't see the problem. It also doesn't have anything to do with React or stateless components – Icepickle Sep 18 '19 at 21:25
  • 1
    Linting errors don't always equate to runtime errors. Linting is used to enforce code style guidelines as well as things that could be actual errors. – Cruril Sep 18 '19 at 21:48
  • 1
    "*Based on the eslint error I receive*" - **what** error do you receive? Please post the message. – Bergi Sep 18 '19 at 22:08

2 Answers2

2

The reason the code works is because the body of doStuff isn't executed until it's invoked. Since doubleNumber happened to be defined before doStuff was called, everything is fine, but the code is correctly identified as brittle by the linter because the dependency inversion.

The crash occurs only if you happened not to initialize doubleNumber by the time doStuff was called:

const doStuff = () => doubleNumber(4);
doStuff(); // ReferenceError: doubleNumber is not defined
const doubleNumber = number => number * 2;

which seems obvious here, but in more complex cases may not be so clear.

const versus let should have no bearing on the linter's output because although they're hoisted, they cannot be accessed until initialized.

The order can be whatever you like, assuming calls are only made once dependencies are available, but that doesn't make it a good idea (which is exactly what linting is supposed to identify).

ggorlen
  • 44,755
  • 7
  • 76
  • 106
1

Your case is described in the Typescript documentation of Block scoped variables (navigate to the last bit of that section).

It says :

you can still capture a block-scoped variable before it’s declared. The only catch is that it’s illegal to call that function before the declaration. If targeting ES2015, a modern runtime will throw an error; however, right now TypeScript is permissive and won’t report this as an error.

And the example given is

function foo() {
    // okay to capture 'a'
    return a;
}

// illegal call 'foo' before 'a' is declared
// runtimes should throw an error here
foo();

let a;

In your case, doubleNumber is being captured inside doStuff, but doubleNumber is declared before doStuff and hence is okay according to the docs, just like the variable a in the documentation's example.

Vandesh
  • 6,368
  • 1
  • 26
  • 38