80

I'm not sure why but it seems that I can't call the let or const variables if I declare them in an if/else statement.

if (withBorder) {
  const classes = `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;
} else {
  const classes = `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;
}
return (
  <div className={classes}>
    {renderedResult}
  </div>
);

If I use this code it says that classes is not defined.

But if I change the const to var classes is defined but I get a warning about classes used outside of binding contextand all var declarations must be at the top of the function scope

How could I fix this?

Wasi Ahmad
  • 35,739
  • 32
  • 114
  • 161
Johhan Santana
  • 2,336
  • 5
  • 33
  • 61
  • 6
    `let` and `const` have been designed to be scoped inside control statements such as `if` and `for`. If you want to use `let` you need to define the variable before the `if` and just assign it inside. – UnholySheep Nov 29 '16 at 22:37
  • that's expected in most programming languages. older javascript will still let you get away with it. However, if it's a constant, the value should be assigned once and never change... declare it as a `let` outside the if block and set it inside the if/else blocks. – ps2goat Nov 29 '16 at 22:38
  • 1
    Why not just use *var*? – RobG Nov 29 '16 at 22:44
  • 5
    @RobG it's not ES6y enough… – Bergi Nov 29 '16 at 22:46
  • 2
    @Bergi—I'm gonna use that, especially with a Kiwi accent. :-) – RobG Nov 29 '16 at 22:47

7 Answers7

65

This is a good example of where a simple ternary assignment could suffice:

const classes = withBorder ?
 `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center` : 
 `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`

As specified in other comments/answers let and const are block scoped, so that's why they don't work in your example.

For DRYer code, you can use the ternary only for the part that depends on it:

 const classes = (withBorder ? `${styles.circularBorder} ` : "") +
 `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`
Gust van de Wal
  • 5,211
  • 1
  • 24
  • 48
maioman
  • 18,154
  • 4
  • 36
  • 42
53

let and const are block level scoped meaning they can only be used within the block they have been defined in ie. { // if defined in here can only be used here }

In this case I would just define above the if/else statement

let classes;

if (withBorder) {
  classes = `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;
} else {
  classes = `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;
}
finalfreq
  • 6,830
  • 2
  • 27
  • 28
  • 13
    This can't be done with const, as const needs an initializer value – yunzen Jun 13 '18 at 07:32
  • 1
    The example uses `let` since we are redefining a variable based on if withBorder exists only way to use `const` would to be with a ternary operator since you can't redefine a constant variable – finalfreq Jun 13 '18 at 16:21
38

Alternative (not sure it's good nor elegant though):

const classes = (() => {
  if (withBorder) {
    return `${styles.circularBorder}...`;
  } else {
    return `${styles.dimensions}...`;
  }
})();
rap-2-h
  • 30,204
  • 37
  • 167
  • 263
5

Don't use an if-else-statement but a ternary expression:

const classes = withBorder
  ? `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`
  :                          `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;

Alternatively, just declare it outside of the if block, which allows you to get rid of the duplication as well:

let classes = `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;
if (withBorder) {
  classes += ` ${styles.circularBorder}`;
  // or if you care about the order,
  // classes = `${styles.circularBorder} ${classes}`;
}

Also have a look at messy classnames construction.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Someone who understands how the judicial use of whitespace can make code easier to understand! (Boo, Prettier.) – David Hull Jul 27 '23 at 21:41
4

let and const are block level scoped, so you will have to define them outside of the block. var works because it hoists out.

You can defined classes before the if block like @finalfreq

or

let classes = `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`;

if (withBorder) {
  classes += `${styles.circularBorder}`;
}
Community
  • 1
  • 1
antyang
  • 51
  • 4
3

ESLint standard likes the operators at the beginning of the line. Long conditionals should also be abstracted out, unless in a computer time loop.

In this particular case the strings are also long, so I would abstract those out too. Problem with Bergi's way is most linters will cripple his style, for conformance reasons.

This way keeps everything normal and easy to read, if you are familiar with ternaries, which you should be.

const styleWithBorder = `${styles.circularBorder} ${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`
const styleWithoutBorder = `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`
const classes = isBorderedElement ? [ styleWithBorder ] : [ styleWithoutBorder ]
Ray Foss
  • 3,649
  • 3
  • 30
  • 31
0

Simple, just do this:

const genericStyle = `${styles.dimensions} ${styles.circularPadding} row flex-items-xs-middle flex-items-xs-center`,

classes = withBorder ? `${styles.circularBorder} ${genericStyle}` : genericStyle;

return (
  <div className={classes}>
    {renderedResult}
  </div>
);

This has some clean-up also, the class used twice and only circularBorder is the difference...

Alireza
  • 100,211
  • 27
  • 269
  • 172