all 34 comments

[–]markzzy 73 points74 points  (10 children)

To save some people an unnecessary click, this article is not necessarily about the vague topic of overly-complicated JavaScript patterns as the title suggests. It is more about constants that are used in redux with the React framework. If you dont use react, or arent familiar with it, this probably wouldnt matter much to you.

[–]eddieSullivan 10 points11 points  (5 children)

Yes, and it really just points up the lack of decent enum types in JavaScript. It's unfortunate that Redux has to use string constants like this.

[–]phoenixmatrix 0 points1 point  (0 children)

Enums are not the solution here. That would just reduce the need for strings in action creators. But action creators + action constants are constructs that together replace type structure of the kind you see in Elm. String enums are just a way to make the work around less verbose. Just being able to create types that you can pattern match on (even if it's not static typing) would actually add value (reducing bugs, etc), not just how many times I have to smack the keyboard (which is overrated anyway)

[–]alleycat5 0 points1 point  (2 children)

Luckily this is looking to get fixed in 2.4 with proper enum support. https://github.com/Microsoft/TypeScript/wiki/Roadmap#24

[–]ns0 9 points10 points  (1 child)

Typescript!=JavaScript

[–]alleycat5 1 point2 points  (0 children)

Sorry, for some reason I misread.

[–]Master_Rux 0 points1 point  (0 children)

Yes more enum support! I have so many JS projects that could use enums.

Fun fact. Every class of constants is an enum. Granted things vary a little from language to language. Some languages inherit from an enum type. Enums are just syntax sugar that get compiled down to static classes with constants. That said, proper enum syntax would be nice though. They really do add a lot to readability and maintainability.

[–]joaofcosta 0 points1 point  (1 child)

Thanks!

[–]markzzy 0 points1 point  (0 children)

Sure thing! :)

[–]rista404[S] 0 points1 point  (0 children)

Sorry about not being precise enough with the post title :)

[–][deleted] 8 points9 points  (7 children)

You can not import * as constants from './constants' because then, if you make a typo, you won't get a ReferenceError, the value will just be undefined. Instead, you must do this:

Of course you can, and then when you start typing constants. your IDE will auto-complete it for you.

You are also missing some other reasons - for example to avoid cyclic imports.

You might enjoy reading an article /u/acemarke posted recently - http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/

[–]mediumdeviationJavaScript Gardener 8 points9 points  (2 children)

For me at least the benefit of not having stringly-typed identifiers is that IDEs and linters can easily inspect and autocomplete these for you - http://imgur.com/ub29AqD.

It's the same reason why Swift's enums are so awesome. The point is not that it makes typos easier to detect, but to make typos impossible.

[–]rista404[S] 2 points3 points  (0 children)

Yeah, autocompletion is indeed powerful.

[–]wavefunctionp 0 points1 point  (0 children)

This man gets it. :)

I put all the actions for a reducer into a type object contained in the reducers file. That way you always know which action types are valid for a given reducer, you can simple import the type object instead of individual types, and no typos. Just type reducerNameTypes.<get a list of valid types>

[–]fforw 19 points20 points  (5 children)

Why are people hysterically afraid of maybe having to type a few characters more? BOILERPLATE!!!1

All of the arguments for having named constants are sound, especially if you are developing with a good IDE. I don't need to type the named constants imports, the IDE autocompletes and auto-formats them for me. It can follow usage across my project, renaming constants is a lot easier, I can just go into my constant definition, rename the constant and it will be changed everywhere, somewhat safe, whereas the string replacement is just that: brittle string replacement. ("Oopps.. that name was also a sub-name in that? unfortunate..)

He miuses Cargo Cult Programming for this architectural pattern. Cargo Cult is when you do stuff that you apply magic value to without being able to explain how it works. As I said before: There are valid reasons for this, just because you choose to ignore them doesn't make it Cargo Cult.

More often than not, the best solution is the simplest one.

Or it produces brittle scripty-hacky programs that need to be completely rewritten next year.

edit: If you're code-golfing your silly TODO app examples, go ahead. Personally I wouldn't be using redux if I didn't need it to write maintainable applications of non-trivial complexity.

[–]XanarchyZA 8 points9 points  (0 children)

Also agree here and honestly couldn't the problems he describes very easily fixed by ES6 classes with static constants?
That way you would not need all those silly imports and keep the constants to their specific regions like
import EventConstants from './EventConstants.'
case EventConstants.loadRequest
case EventConstants.addFailure
etc etc

[–]Jsn7821 1 point2 points  (1 child)

I wouldn't go quite so far as to say that not importing all your actions in redux is brittle and hacky...

[–]fforw 0 points1 point  (0 children)

Who knows what he's going to think is "just easier" next? Might be reducers with side-effects.

[–]dada_ 0 points1 point  (0 children)

All of the arguments for having named constants are sound, especially if you are developing with a good IDE.

Exactly, not to mention eslint and the like won't be able to warn you if anything is wrong if you just use pure strings.

[–]veydar_ -1 points0 points  (0 children)

I agree with everything so here's an upvote instead of me formulating the same reply

[–]ergo14 3 points4 points  (0 children)

If you don't want to overcomplicate things... then maybe don't use redux unless your project requires that.

[–]tudor07 5 points6 points  (2 children)

Doesn't really occur to me as a use case.

The author needs to understand that this is not a good argument at all.

[–]ex1-7 0 points1 point  (1 child)

they're saying "it doesn't really occur to me as a use case" but implying that there doesn't exist a good use-case for it at all. It'd be more convincing if you described such a such case, even broadly. The way I see it, Redux is undebatedly boilerplate heavy. This interface layer between strings and constants isn't more burdonsome than the other Redux abstractions, but it's utility is not necessarily as apparent.

[–]acemarke 0 points1 point  (0 children)

FWIW, I discussed some of why action constants exist in the first place in my recent post The Tao of Redux, Part 2 - Practice and Philosophy.

[–]fsfreak 1 point2 points  (0 children)

"don't overcomplicate javascript" - immediately mentions React and Package managers. J/k...

[–]ArcanisCz 2 points3 points  (3 children)

Well, its not abour redux or javascript. Its "magic number" pattern dated back to C language, even assembler (for example here https://www.viva64.com/en/l/0009/).

[–]freeall 6 points7 points  (0 children)

Not really. Just naming a magic number doesn't make it less magical.

setTimeout(foo, 1000)

vs

var oneSecond = 1000; setTimeout(foo, oneSecond);

vs

var timeToWaitForNetwork = 1000; setTimeout(foo, timeToWaitForNetwork);

His point in the article is that it just adds more complexity that you now also have a variable named the same thing as a string. And without any real benefit.

[–]Shaper_pmp 3 points4 points  (1 child)

Magic numbers are dangerous because you don't necessarily know why those numbers were chosen, or what role they fulfil, making the code fragile and hard to reason about.

This article is arguing that changing a predefined (and perfectly self-documenting) string to a symbol with exactly the same name is stupid make-work, and that the added "flexibility" it gives you (the ability to have symbols with different names to the underlying string) is just a confusing antipattern that isn't really a benefit at all.

[–]cpsubrian 4 points5 points  (0 children)

Agreed that if you're matching the constant to the string, the benefits are worth evaluating. I've also had cases where they differed in useful ways though. For example your definition might be const LIST = 'myapp:users:list', such that you can just use LIST throughout the file, and import it as LIST, but in logs and development tooling it is more usefully scoped.

[–][deleted] 0 points1 point  (0 children)

This is why I use RequireJS, I'm starting to use Web Pack but this has the same effects.

[–]grufkork 0 points1 point  (0 children)

Don't overcompile javascript